Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
mdoc_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_WITNESS_H_
16#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_WITNESS_H_
17
18#include <stddef.h>
19#include <string.h>
20
21#include <array>
22#include <cstdint>
23#include <iterator>
24#include <vector>
25
26#include "arrays/dense.h"
27#include "cbor/host_decoder.h"
28#include "circuits/ecdsa/verify_witness.h"
29#include "circuits/logic/bit_plucker_encoder.h"
30#include "circuits/mac/mac_witness.h"
31#include "circuits/mdoc/mdoc_constants.h"
32#include "circuits/mdoc/mdoc_hash.h"
33#include "circuits/mdoc/mdoc_zk.h"
34#include "circuits/sha/flatsha256_witness.h"
35#include "gf2k/gf2_128.h"
36#include "util/crypto.h"
37#include "util/log.h"
38#include "util/panic.h"
39
40
41namespace proofs {
42
43struct CborIndex {
44 size_t k, v, ndx;
45 size_t pos, len; /* optional fields for string/byte values */
46};
47
48struct AttrShift {
49 size_t offset, len;
50};
51
52// This class represents an attribute that is parsed out of the deviceResponse
53// data structure. It includes offsets into the original mdoc which can be used
54// to construct SHA witnesses for disclosing the value of an attribute.
56 // Offset and length of the attribute identifier and attribute value.
57 size_t id_ind;
58 size_t id_len;
59 size_t val_ind;
60 size_t val_len;
61
62 // Index for this attribute among all attributes in the mdoc hash list.
63 size_t digest_id;
64 CborIndex mso;
65
66 // Offset and length of the attribute tag.
67 size_t tag_ind;
68 size_t tag_len;
69
70 // The original mdoc into which all offsets point.
71 const uint8_t* doc;
72
73 bool operator==(const RequestedAttribute& y) const {
74 return y.id_len == id_len && memcmp(y.id, &doc[id_ind], id_len) == 0;
75 }
76
77 size_t witness_length(const RequestedAttribute& attr) {
78 size_t r = id_len + val_len + 1 + 12;
79 if (attr.type == CborAttributeType::kDate) {
80 r += 4;
81 } else if (attr.type == CborAttributeType::kString ||
82 attr.type == CborAttributeType::kBytes) {
83 r += 1;
84 if (val_len > 23) r++;
85 if (val_len > 255) r++;
86 }
87 return r;
88 }
89};
90
92 public:
93 // Various cbor indices/witnesses for intermediate structures.
94 CborIndex t_mso_, sig_, dksig_;
95 CborIndex valid_, valid_from_, valid_until_;
96 CborIndex dev_key_info_, dev_key_, dev_key_pkx_, dev_key_pky_;
97 CborIndex value_digests_, org_;
98 std::vector<FullAttribute> attributes_;
99 std::vector<uint8_t> doc_type_;
100
101 // These are the exact bytes which produce the hash that is signed.
102 std::vector<uint8_t> tagged_mso_bytes_;
103
104 /*
105 Parses a byte representation of the "DeviceResponse" string from a phone.
106 This contains all of the information needed to respond to an mdoc verifier.
107 This method attempts to construct a witness from this.
108
109 8.3.2.1.2.2: first field is "version", 2nd optional field is "documents"
110 [documents][0][issuerSigned][issuerAuth]{2} --> tagged mso
111 [documents][0][issuerSigned][issuerAuth]{3} --> issuer sig
112 [documents][0][issuerSigned][nameSpaces][ns][index-of-attr] --> enc attr
113 [documents][0][deviceSigned][deviceAuth][deviceSignature][3] --> sig
114
115 This method produces indices into doc as state.
116 */
117 bool parse_device_response(size_t len, const uint8_t resp[/* len */]) {
118 size_t np = 0;
119 // When this object falls out of scope, all parsing objects will be
120 // garbage collected.
121 CborDoc root;
122 bool ok = root.decode(resp, len, np, 0);
123 if (!ok) {
124 log(ERROR, "Failed to decode root");
125 return false;
126 }
127
128 size_t di;
129 auto docs = root.lookup(resp, 9, (uint8_t*)"documents", di);
130 if (docs == nullptr) return false;
131 // Fields of Document are "docType", "issuerSigned", "deviceSigned", ?errors
132
133 auto docs0 = docs[1].index(0);
134 if (docs0 == nullptr) return false;
135
136 auto dt = docs0[0].lookup(resp, 7, (uint8_t*)"docType", di);
137 if (dt == nullptr) return false;
138 doc_type_.insert(doc_type_.begin(), resp + dt[1].u_.string.pos,
139 resp + dt[1].u_.string.pos + dt[1].u_.string.len);
140
141 // The returned docs0 is the map, so index at [0].
142 auto is = docs0[0].lookup(resp, 12, (uint8_t*)"issuerSigned", di);
143 if (is == nullptr) return false;
144
145 auto ia = is[1].lookup(resp, 10, (uint8_t*)"issuerAuth", di);
146 if (ia == nullptr) return false;
147
148 auto tmso = ia[1].index(2);
149 if (tmso == nullptr) return false;
150 copy_header(t_mso_, tmso);
151 auto nsig = ia[1].index(3);
152 if (nsig == nullptr) return false;
153 copy_header(sig_, nsig);
154
155 auto ns = is[1].lookup(resp, 10, (uint8_t*)"nameSpaces", di);
156 if (ns == nullptr) return false;
157
158 // Find the attribute witness we need from here.
159 // For now, we only support 1 namespace.
160 auto mldns = ns[1].lookup(resp, 17, (uint8_t*)"org.iso.18013.5.1", di);
161 if (mldns == nullptr) return false;
162 size_t ai = 0;
163 auto tattr = mldns[1].index(ai++);
164 while (tattr != nullptr) {
165 CborDoc er;
166 // Decode the map in this tagged attribute.
167 size_t pos = tattr->children_[0].u_.string.pos;
168 size_t end = pos + tattr->children_[0].u_.string.len;
169 if (!er.decode(resp, end, pos, 0)) {
170 return false;
171 }
172
173 auto ei = er.lookup(resp, 17, (uint8_t*)"elementIdentifier", di);
174 if (ei == nullptr) return false;
175 auto ev = er.lookup(resp, 12, (uint8_t*)"elementValue", di);
176 if (ev == nullptr) return false;
177 auto digid = er.lookup(resp, 8, (uint8_t*)"digestID", di);
178 if (digid == nullptr) return false;
179
180 attributes_.push_back(
181 (FullAttribute){ei[1].position(),
182 ei[1].length(),
183 ev[1].position(),
184 ev[1].length(),
185 static_cast<size_t>(digid[1].u_.u64), /* digest_id */
186 {0, 0, 0}, /* default mso_ind */
187 tattr->header_pos_, /* tag_ind */
188 tattr->children_[0].u_.string.len +
189 4, /* +4 for the D8 18 58 <> prefix */
190 resp});
191
192 tattr = mldns[1].index(ai++);
193 }
194
195 auto ds = docs0[0].lookup(resp, 12, (uint8_t*)"deviceSigned", di);
196 if (ds == nullptr) return false;
197 auto da = ds[1].lookup(resp, 10, (uint8_t*)"deviceAuth", di);
198 if (da == nullptr) return false;
199 auto dsi = da[1].lookup(resp, 15, (uint8_t*)"deviceSignature", di);
200 if (dsi == nullptr) return false;
201 auto ndksig = dsi[1].index(3);
202 if (ndksig == nullptr) return false;
203 copy_header(dksig_, ndksig);
204
205 // Then parse tagged mso. Skip 5 bytes to skip the D8 18 59 <len2>.
206 const uint8_t* pmso = resp + tmso->u_.string.pos + 5;
207 size_t pos = 0;
208 CborDoc mso;
209 if (!mso.decode(pmso, tmso->u_.string.len - 5, pos, 0)) return false;
210 auto nv = mso.lookup(pmso, kValidityInfoLen, kValidityInfoID, valid_.ndx);
211 if (nv == nullptr) return false;
212 copy_kv_header(valid_, nv);
213
214 auto nvf = nv[1].lookup(pmso, kValidFromLen, kValidFromID, valid_from_.ndx);
215 if (nvf == nullptr) return false;
216 copy_kv_header(valid_from_, nvf);
217
218 auto nvu =
219 nv[1].lookup(pmso, kValidUntilLen, kValidUntilID, valid_until_.ndx);
220 if (nvu == nullptr) return false;
221 copy_kv_header(valid_until_, nvu);
222
223 auto ndki = mso.lookup(pmso, kDeviceKeyInfoLen, kDeviceKeyInfoID,
224 dev_key_info_.ndx);
225 if (ndki == nullptr) return false;
226 copy_kv_header(dev_key_info_, ndki);
227
228 auto ndk = ndki[1].lookup(pmso, kDeviceKeyLen, kDeviceKeyID, dev_key_.ndx);
229 if (ndk == nullptr) return false;
230 copy_kv_header(dev_key_, ndk);
231
232 auto npkx = ndk[1].lookup_negative(-1, dev_key_pkx_.ndx);
233 if (npkx == nullptr) return false;
234 copy_kv_header(dev_key_pkx_, npkx);
235
236 auto npky = ndk[1].lookup_negative(-2, dev_key_pky_.ndx);
237 if (npky == nullptr) return false;
238 copy_kv_header(dev_key_pky_, npky);
239
240 auto nvd =
241 mso.lookup(pmso, kValueDigestsLen, kValueDigestsID, value_digests_.ndx);
242 if (nvd == nullptr) return false;
243 copy_kv_header(value_digests_, nvd);
244
245 auto norg = nvd[1].lookup(pmso, kOrgLen, kOrgID, org_.ndx);
246 if (norg == nullptr) return false;
247 copy_kv_header(org_, norg);
248
249 for (auto& attr : attributes_) {
250 uint64_t hi = (uint64_t)attr.digest_id;
251 auto hattr = norg[1].lookup_unsigned(hi, attr.mso.ndx);
252 if (hattr == nullptr) return false;
253 copy_kv_header(attr.mso, hattr);
254 }
255
256 tagged_mso_bytes_.assign(std::begin(kCose1Prefix), std::end(kCose1Prefix));
257 // Add 2-byte length
258 tagged_mso_bytes_.push_back((t_mso_.len >> 8) & 0xff);
259 tagged_mso_bytes_.push_back(t_mso_.len & 0xff);
260 for (size_t i = 0; i < t_mso_.len; ++i) {
261 tagged_mso_bytes_.push_back(resp[t_mso_.pos + i]);
262 }
263
264 return true;
265 }
266
267 private:
268 // Used to copy the results of a map lookup.
269 static void copy_kv_header(CborIndex& ind, const CborDoc* n) {
270 ind.k = n[0].header_pos_;
271 ind.v = n[1].header_pos_;
272
273 if (n[1].t_ == TEXT || n[1].t_ == BYTES) {
274 ind.pos = n[1].u_.string.pos;
275 ind.len = n[1].u_.string.len;
276 }
277 }
278
279 // Used to copy the results of an index lookup.
280 static void copy_header(CborIndex& ind, const CborDoc* n) {
281 ind.k = n->header_pos_;
282 ind.pos = n->u_.string.pos;
283 ind.len = n->u_.string.len;
284 }
285};
286
287// Transform from u8 be (i.e., be[31] is the most significant byte) into
288// nat form, which requires first converting to le byte order.
289template <class Nat>
290Nat nat_from_be(const uint8_t be[/* Nat::kBytes */]) {
291 uint8_t tmp[Nat::kBytes];
292 // Transform into byte-wise le representation.
293 for (size_t i = 0; i < Nat::kBytes; ++i) {
294 tmp[i] = be[Nat::kBytes - i - 1];
295 }
296 return Nat::of_bytes(tmp);
297}
298
299// Transform from u32 be (i.e., be[0] is the most significant nibble)
300// into nat form, which requires first converting to le byte order.
301template <class Nat>
302Nat nat_from_u32(const uint32_t be[]) {
303 uint8_t tmp[Nat::kBytes];
304 const size_t top = Nat::kBytes / 4;
305 for (size_t i = 0; i < Nat::kBytes; ++i) {
306 tmp[i] = (be[top - i / 4 - 1] >> ((i % 4) * 8)) & 0xff;
307 }
308 return Nat::of_bytes(tmp);
309}
310
311template <typename Nat>
312Nat nat_from_hash(const uint8_t data[], size_t len) {
313 uint8_t hash[kSHA256DigestSize];
314 SHA256 sha;
315 sha.Update(data, len);
316 sha.DigestData(hash);
317 Nat ne = nat_from_be<Nat>(hash);
318 return ne;
319}
320
321static inline void append_bytes_len(std::vector<uint8_t>& buf, size_t len) {
322 if (len > 256) {
323 uint8_t ll[] = {0x59, (uint8_t)((len >> 8) & 0xff), (uint8_t)(len & 0xff)};
324 buf.insert(buf.end(), ll, ll + 3);
325 } else {
326 uint8_t ll[] = {0x58, static_cast<uint8_t>(len & 0xff)};
327 buf.insert(buf.end(), ll, ll + 2);
328 }
329}
330
331static inline void append_text_len(std::vector<uint8_t>& buf, size_t len) {
332 check(len < 256, "Text length too large");
333 if (len < 24) {
334 buf.push_back(0x60 + len);
335 } else if (len < 255) {
336 buf.push_back(0x78);
337 buf.push_back(len);
338 }
339}
340
341// Form the COSE1 encoding of the DeviceAuthenticationBytes,
342// then compute its SHA-256 hash, and cast into a Nat.
343// The original form follows S9.1.3.4 of the mdoc spec and
344// assumed that the transcript was a simple string.
345// The Jan'2024 demo requires using the "AndroidHandover" version
346// of the DeviceAuthenticationBytes formatting, which is not
347// specified in the spec. As a result, this function is a hack
348// that mimics the bytes produced by the Android com.android.identity.wallet
349// library.
350template <class Nat>
351static Nat compute_transcript_hash(
352 const uint8_t transcript[], size_t len,
353 const std::vector<uint8_t>* docType = nullptr) {
354 // The DeviceAuthenticationBytes is defined in 9.1.3.4 as:
355 // DeviceAuthentication = [
356 // "DeviceAuthentication",
357 // SessionTranscript,
358 // DocType, ; Same as in mdoc response
359 // DeviceNameSpacesBytes ; Same as in mdoc response
360 // ]
361 std::vector<uint8_t> deviceAuthentication = {
362 0x84, 0x74, 'D', 'e', 'v', 'i', 'c', 'e', 'A', 'u', 't',
363 'h', 'e', 'n', 't', 'i', 'c', 'a', 't', 'i', 'o', 'n',
364 };
365 std::vector<uint8_t> docTypeBytes = {
366 0x75, 'o', 'r', 'g', '.', 'i', 's', 'o', '.', '1', '8',
367 '0', '1', '3', '.', '5', '.', '1', '.', 'm', 'D', 'L',
368 };
369 std::vector<uint8_t> deviceNameSpacesBytes = {0xD8, 0x18, 0x41, 0xA0};
370
371 if (docType != nullptr) {
372 docTypeBytes.clear();
373 append_text_len(docTypeBytes, docType->size());
374 docTypeBytes.insert(docTypeBytes.end(), docType->begin(), docType->end());
375 }
376
377 // Provide the DeviceAuthentication bytes
378 std::vector<uint8_t> da(deviceAuthentication);
379 da.insert(da.end(), transcript, transcript + len);
380 da.insert(da.end(), docTypeBytes.begin(), docTypeBytes.end());
381 da.insert(da.end(), deviceNameSpacesBytes.begin(),
382 deviceNameSpacesBytes.end());
383
384 // Form the COSE1 encoding of the DeviceAuthenticationBytes.
385 std::vector<uint8_t> cose1{0x84, 0x6A, 0x53, 0x69, 0x67, 0x6E,
386 0x61, 0x74, 0x75, 0x72, 0x65, 0x31,
387 0x43, 0xA1, 0x01, 0x26, 0x40};
388 uint8_t tag[] = {0xD8, 0x18};
389
390 size_t l1 = da.size();
391 size_t l2 = l1 + (l1 < 256 ? 4 : 5); /* Tagged array length. */
392 append_bytes_len(cose1, l2);
393 cose1.insert(cose1.end(), tag, tag + 2);
394 append_bytes_len(cose1, l1);
395 cose1.insert(cose1.end(), da.begin(), da.end());
396
397 return nat_from_hash<Nat>(cose1.data(), cose1.size());
398}
399
400// Interpret input s as an len*8-bit string, and use it to fill max*8 bits
401// in the dense filler.
402// Pad the input with the Field value 2 to indicate the positions
403// that are not part of the string.
404template <class Field>
405void fill_bit_string(DenseFiller<Field>& filler, const uint8_t s[/*len*/],
406 size_t len, size_t max, const Field& Fs) {
407 std::vector<typename Field::Elt> v(max * 8, Fs.of_scalar(2));
408 for (size_t i = 0; i < max && i < len; ++i) {
409 fill_byte(v, s[i], i, Fs);
410 }
411 filler.push_back(v);
412}
413
414template <class Field>
415void fill_byte(std::vector<typename Field::Elt>& v, uint8_t b, size_t i,
416 const Field& F) {
417 for (size_t j = 0; j < 8; ++j) {
418 v[i*8 + j] = ( b >> j & 0x1 ) ? F.one() : F.zero();
419 }
420}
421
422template <class Field>
423void fill_attribute(DenseFiller<Field> &filler, const RequestedAttribute &attr,
424 const Field &F, size_t version = 2) {
425 if (version <= 2) {
426 fill_bit_string(filler, attr.id, attr.id_len, 32, F);
427 fill_bit_string(filler, attr.value, attr.value_len, 64, F);
428 } else if (version == 3) {
429 // In version 3, the attribute is encoded as the raw cbor string that
430 // included <name of identifier> <elementValue> <attributeValue>
431 std::vector<typename Field::Elt> v(96 * 8, F.of_scalar(0));
432
433 size_t i = 0;
434 for (size_t j = 0; j < attr.id_len && i < 96; ++j, ++i) {
435 fill_byte(v, attr.id[j], i, F);
436 }
437 fill_byte(v, 0x6C, i++, F); // Cbor type for length 12 string.
438 const char* ev = "elementValue";
439 for (size_t j = 0; j < 12 && i < 96; ++j, ++i) {
440 fill_byte(v, ev[j], i, F);
441 }
442 std::vector<uint8_t> vbuf;
443 uint8_t tag[] = {0xD9, 0x03, 0xEC, 0x6A};
444 switch (attr.type) {
445 case CborAttributeType::kPrimitive:
446 vbuf.push_back(attr.value[0]);
447 break;
448 case CborAttributeType::kString:
449 append_text_len(vbuf, attr.value_len);
450 vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
451 break;
452 case CborAttributeType::kBytes:
453 append_bytes_len(vbuf, attr.value_len);
454 vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
455 break;
456 case CborAttributeType::kDate:
457 vbuf.insert(vbuf.end(), tag, tag + 4);
458 vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
459 break;
460 case CborAttributeType::kInt:
461 vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
462 break;
463 }
464 for (size_t j = 0; j < vbuf.size() && i < 96; ++j, ++i) {
465 fill_byte(v, vbuf[j], i, F);
466 }
467 filler.push_back(v);
468 }
469}
470
471
472template <class EC, class ScalarField>
473class MdocSignatureWitness {
474 using Field = typename EC::Field;
475 using Elt = typename Field::Elt;
476 using Nat = typename Field::N;
477 using EcdsaWitness = VerifyWitness3<EC, ScalarField>;
478 using MacWitnessF = MacWitness<Field>;
479 using f_128 = GF2_128<>;
480 const EC& ec_;
481 const f_128& gf_;
482
483 public:
484 Elt e_, e2_; /* Issuer signature values. */
485 Elt dpkx_, dpky_; /* device key */
486 EcdsaWitness ew_, dkw_;
487 MacWitnessF macs_[3]; /* macs for e_, dpkx_, dpky_ */
488
489 explicit MdocSignatureWitness(const EC& ec, const ScalarField& Fn,
490 const f_128& gf)
491 : ec_(ec),
492 gf_(gf),
493 ew_(Fn, ec),
494 dkw_(Fn, ec),
495 macs_{MacWitnessF(ec.f_, gf_), MacWitnessF(ec.f_, gf_),
496 MacWitnessF(ec.f_, gf_)} {}
497
498 void fill_witness(DenseFiller<Field>& filler) const {
499 filler.push_back(e_);
500 filler.push_back(dpkx_);
501 filler.push_back(dpky_);
502
503 ew_.fill_witness(filler);
504 dkw_.fill_witness(filler);
505 for (auto& mac : macs_) {
506 mac.fill_witness(filler);
507 }
508 }
509
510 bool compute_witness(Elt pkX, Elt pkY, const uint8_t mdoc[/* len */],
511 size_t len, const uint8_t transcript[/* tlen */],
512 size_t tlen) {
513 ParsedMdoc pm;
514
515 if (!pm.parse_device_response(len, mdoc)) {
516 return false;
517 }
518
519 Nat ne = nat_from_hash<Nat>(pm.tagged_mso_bytes_.data(),
520 pm.tagged_mso_bytes_.size());
521 e_ = ec_.f_.to_montgomery(ne);
522
523 // Parse (r,s).
524 const size_t l = pm.sig_.len;
525 Nat nr = nat_from_be<Nat>(&mdoc[pm.sig_.pos]);
526 Nat ns = nat_from_be<Nat>(&mdoc[pm.sig_.pos + l / 2]);
527 ew_.compute_witness(pkX, pkY, ne, nr, ns);
528
529 Nat ne2 = compute_transcript_hash<Nat>(transcript, tlen, &pm.doc_type_);
530 const size_t l2 = pm.dksig_.len;
531 Nat nr2 = nat_from_be<Nat>(&mdoc[pm.dksig_.pos]);
532 Nat ns2 = nat_from_be<Nat>(&mdoc[pm.dksig_.pos + l2 / 2]);
533 size_t pmso = pm.t_mso_.pos + 5; /* skip the tag */
534 dpkx_ = ec_.f_.to_montgomery(
535 nat_from_be<Nat>(&mdoc[pmso + pm.dev_key_pkx_.pos]));
536 dpky_ = ec_.f_.to_montgomery(
537 nat_from_be<Nat>(&mdoc[pmso + pm.dev_key_pky_.pos]));
538 e2_ = ec_.f_.to_montgomery(ne2);
539 dkw_.compute_witness(dpkx_, dpky_, ne2, nr2, ns2);
540 return true;
541 }
542};
543
544// EC: implements the elliptic curve for the mdoc
545// Field: implements the field used to define the sumcheck circuit, which can
546// be smaller than the EC field
547template <typename EC, typename Field>
548class MdocHashWitness {
549 using ECField = typename EC::Field;
550 using ECElt = typename ECField::Elt;
551 using ECNat = typename ECField::N;
552 using Elt = typename Field::Elt;
553 using vindex = std::array<Elt, kCborIndexBits>;
554
555 const EC& ec_;
556 const Field& fn_;
557
558 public:
559 ECElt e_; /* Issuer signature values. */
560 ECElt dpkx_, dpky_; /* device key */
561 uint8_t signed_bytes_[kMaxSHABlocks * 64];
562 uint8_t numb_; /* Number of the correct sha block. */
563
564 size_t num_attr_;
565 std::vector<std::vector<uint8_t>> attr_bytes_;
566 std::vector<std::vector<FlatSHA256Witness::BlockWitness>> atw_;
567
568 std::vector<uint8_t> attr_n_; /* All attributes currently require 2 SHA. */
569 std::vector<CborIndex> attr_mso_; /* The cbor indices of the attributes. */
570 std::vector<AttrShift> attr_ei_;
571 std::vector<AttrShift> attr_ev_;
572
573 FlatSHA256Witness::BlockWitness bw_[kMaxSHABlocks];
574
575 ParsedMdoc pm_;
576
577 uint8_t now_[20]; /* CBOR-formatted time used for expiry comparison. */
578
579 explicit MdocHashWitness(size_t num_attr, const EC& ec, const Field& Fn)
580 : ec_(ec), fn_(Fn), num_attr_(num_attr) {}
581
582 void fill_cbor_index(DenseFiller<Field>& df, const CborIndex& ind) const {
583 df.push_back(ind.k, kCborIndexBits, fn_);
584 }
585
586 void fill_attr_shift(DenseFiller<Field>& df, const AttrShift& attr) const {
587 df.push_back(attr.offset, kCborIndexBits, fn_);
588 df.push_back(attr.len, kCborIndexBits, fn_);
589 }
590
591 void fill_sha(DenseFiller<Field>& filler,
592 const FlatSHA256Witness::BlockWitness& bw) const {
594 for (size_t k = 0; k < 48; ++k) {
595 filler.push_back(BPENC.mkpacked_v32(bw.outw[k]));
596 }
597 for (size_t k = 0; k < 64; ++k) {
598 filler.push_back(BPENC.mkpacked_v32(bw.oute[k]));
599 filler.push_back(BPENC.mkpacked_v32(bw.outa[k]));
600 }
601 for (size_t k = 0; k < 8; ++k) {
602 filler.push_back(BPENC.mkpacked_v32(bw.h1[k]));
603 }
604 }
605
606 void fill_witness(DenseFiller<Field>& filler) const {
607 // Fill sha of main mso.
608 filler.push_back(numb_, 8, fn_);
609 // Don't push the prefix.
610 for (size_t i = kCose1PrefixLen; i < kMaxSHABlocks * 64; ++i) {
611 filler.push_back(signed_bytes_[i], 8, fn_);
612 }
613 for (size_t j = 0; j < kMaxSHABlocks; j++) {
614 fill_sha(filler, bw_[j]);
615 }
616 // === done with sha
617
618 fill_cbor_index(filler, pm_.valid_from_);
619 fill_cbor_index(filler, pm_.valid_until_);
620 fill_cbor_index(filler, pm_.dev_key_info_);
621 fill_cbor_index(filler, pm_.value_digests_);
622
623 // Fill all attribute witnesses.
624 for (size_t ai = 0; ai < num_attr_; ++ai) {
625 for (size_t i = 0; i < 2 * 64; ++i) {
626 filler.push_back(attr_bytes_[ai][i], 8, fn_);
627 }
628 for (size_t j = 0; j < 2; j++) {
629 fill_sha(filler, atw_[ai][j]);
630 }
631
632 // In the case of attribute mso, push the value to avoid having to
633 // deal with 1- or 2- byte key length.
634 filler.push_back(attr_mso_[ai].v, kCborIndexBits, fn_);
635 fill_attr_shift(filler, attr_ei_[ai]);
636 fill_attr_shift(filler, attr_ev_[ai]);
637 }
638 }
639
640 bool compute_witness(const uint8_t mdoc[/* len */], size_t len,
641 const uint8_t transcript[/* tlen */], size_t tlen,
642 const RequestedAttribute attrs[], size_t attrs_len,
643 const uint8_t tnow[/*20*/], size_t version = 2) {
644 if (!pm_.parse_device_response(len, mdoc)) {
645 log(ERROR, "Failed to parse device response");
646 return false;
647 }
648
649 std::vector<uint8_t> buf;
650 if (pm_.t_mso_.len >= kMaxSHABlocks * 64 - 9 - kCose1PrefixLen) {
651 log(ERROR, "tagged mso is too big: %zu", pm_.t_mso_.len);
652 return false;
653 }
654
655 buf.assign(std::begin(kCose1Prefix), std::end(kCose1Prefix));
656 // Add 2-byte length
657 buf.push_back((pm_.t_mso_.len >> 8) & 0xff);
658 buf.push_back(pm_.t_mso_.len & 0xff);
659 for (size_t i = 0; i < pm_.t_mso_.len; ++i) {
660 buf.push_back(mdoc[pm_.t_mso_.pos + i]);
661 }
662
663 FlatSHA256Witness::transform_and_witness_message(
664 buf.size(), buf.data(), kMaxSHABlocks, numb_, signed_bytes_, bw_);
665
666 ECNat ne = nat_from_u32<ECNat>(bw_[numb_ - 1].h1);
667 e_ = ec_.f_.to_montgomery(ne);
668
669 memcpy(now_, tnow, 20);
670
671 size_t pmso = pm_.t_mso_.pos + 5; /* +5 to skip the tag */
672 dpkx_ = ec_.f_.to_montgomery(
673 nat_from_be<ECNat>(&mdoc[pmso + pm_.dev_key_pkx_.pos]));
674 dpky_ = ec_.f_.to_montgomery(
675 nat_from_be<ECNat>(&mdoc[pmso + pm_.dev_key_pky_.pos]));
676
677 // initialize variables
678 attr_n_.resize(attrs_len);
679 attr_mso_.resize(attrs_len);
680 attr_ev_.resize(attrs_len);
681 attr_ei_.resize(attrs_len);
682 attr_bytes_.resize(attrs_len);
683 atw_.resize(attrs_len);
684
685 // Match the attributes with the witnesses from the deviceResponse.
686 for (size_t i = 0; i < attrs_len; ++i) {
687 attr_bytes_[i].resize(128);
688 atw_[i].resize(2);
689 bool found = false;
690 for (auto fa : pm_.attributes_) {
691 if (fa == attrs[i]) {
692 FlatSHA256Witness::transform_and_witness_message(
693 fa.tag_len, &fa.doc[fa.tag_ind], 2, attr_n_[i],
694 &attr_bytes_[i][0], &atw_[i][0]);
695 attr_mso_[i] = fa.mso;
696 attr_ei_[i].offset = fa.id_ind - fa.tag_ind;
697 attr_ei_[i].len = fa.id_len;
698 if (version > 2) {
699 attr_ei_[i].len = fa.witness_length(attrs[i]);
700 }
701 attr_ev_[i].offset = fa.val_ind - fa.tag_ind;
702 attr_ev_[i].len = fa.val_len;
703 found = true;
704 break;
705 }
706 }
707 if (!found) {
708 log(ERROR, "Could not find attribute %.*s", attrs[i].id_len,
709 attrs[i].id);
710 return false;
711 }
712 }
713 return true;
714 }
715};
716} // namespace proofs
717
718#endif // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_WITNESS_H_
Definition bit_plucker_encoder.h:27
Definition host_decoder.h:47
Definition dense.h:153
Definition gf2_128.h:35
Definition mac_witness.h:28
Definition nat.h:60
Definition mdoc_witness.h:91
Definition crypto.h:40
Definition verify_witness.h:30
Definition mdoc_zk.h:37
Definition mdoc_witness.h:48
Definition mdoc_witness.h:43
Definition flatsha256_witness.h:27
Definition mdoc_witness.h:55
Definition gf2_128.h:63