15#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_WITNESS_H_
16#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_WITNESS_H_
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"
38#include "util/panic.h"
74 return y.id_len == id_len && memcmp(y.id, &doc[id_ind], id_len) == 0;
78 size_t r = id_len + val_len + 1 + 12;
79 if (attr.type == CborAttributeType::kDate) {
81 }
else if (attr.type == CborAttributeType::kString ||
82 attr.type == CborAttributeType::kBytes) {
84 if (val_len > 23) r++;
85 if (val_len > 255) r++;
95 CborIndex valid_, valid_from_, valid_until_;
96 CborIndex dev_key_info_, dev_key_, dev_key_pkx_, dev_key_pky_;
98 std::vector<FullAttribute> attributes_;
99 std::vector<uint8_t> doc_type_;
102 std::vector<uint8_t> tagged_mso_bytes_;
117 bool parse_device_response(
size_t len,
const uint8_t resp[]) {
122 bool ok = root.decode(resp, len, np, 0);
124 log(ERROR,
"Failed to decode root");
129 auto docs = root.lookup(resp, 9, (uint8_t*)
"documents", di);
130 if (docs ==
nullptr)
return false;
133 auto docs0 = docs[1].index(0);
134 if (docs0 ==
nullptr)
return false;
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);
142 auto is = docs0[0].lookup(resp, 12, (uint8_t*)
"issuerSigned", di);
143 if (is ==
nullptr)
return false;
145 auto ia = is[1].lookup(resp, 10, (uint8_t*)
"issuerAuth", di);
146 if (ia ==
nullptr)
return false;
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);
155 auto ns = is[1].lookup(resp, 10, (uint8_t*)
"nameSpaces", di);
156 if (ns ==
nullptr)
return false;
160 auto mldns = ns[1].lookup(resp, 17, (uint8_t*)
"org.iso.18013.5.1", di);
161 if (mldns ==
nullptr)
return false;
163 auto tattr = mldns[1].index(ai++);
164 while (tattr !=
nullptr) {
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)) {
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;
180 attributes_.push_back(
185 static_cast<size_t>(digid[1].u_.u64),
188 tattr->children_[0].u_.string.len +
192 tattr = mldns[1].index(ai++);
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);
206 const uint8_t* pmso = resp + tmso->u_.string.pos + 5;
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);
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);
219 nv[1].lookup(pmso, kValidUntilLen, kValidUntilID, valid_until_.ndx);
220 if (nvu ==
nullptr)
return false;
221 copy_kv_header(valid_until_, nvu);
223 auto ndki = mso.lookup(pmso, kDeviceKeyInfoLen, kDeviceKeyInfoID,
225 if (ndki ==
nullptr)
return false;
226 copy_kv_header(dev_key_info_, ndki);
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);
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);
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);
241 mso.lookup(pmso, kValueDigestsLen, kValueDigestsID, value_digests_.ndx);
242 if (nvd ==
nullptr)
return false;
243 copy_kv_header(value_digests_, nvd);
245 auto norg = nvd[1].lookup(pmso, kOrgLen, kOrgID, org_.ndx);
246 if (norg ==
nullptr)
return false;
247 copy_kv_header(org_, norg);
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);
256 tagged_mso_bytes_.assign(std::begin(kCose1Prefix), std::end(kCose1Prefix));
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]);
270 ind.k = n[0].header_pos_;
271 ind.v = n[1].header_pos_;
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;
281 ind.k = n->header_pos_;
282 ind.pos = n->u_.string.pos;
283 ind.len = n->u_.string.len;
290Nat nat_from_be(
const uint8_t be[]) {
291 uint8_t tmp[Nat::kBytes];
293 for (
size_t i = 0; i < Nat::kBytes; ++i) {
294 tmp[i] = be[Nat::kBytes - i - 1];
296 return Nat::of_bytes(tmp);
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;
308 return Nat::of_bytes(tmp);
311template <
typename Nat>
312Nat nat_from_hash(
const uint8_t data[],
size_t len) {
313 uint8_t hash[kSHA256DigestSize];
315 sha.Update(data, len);
316 sha.DigestData(hash);
317 Nat ne = nat_from_be<Nat>(hash);
321static inline void append_bytes_len(std::vector<uint8_t>& buf,
size_t len) {
323 uint8_t ll[] = {0x59, (uint8_t)((len >> 8) & 0xff), (uint8_t)(len & 0xff)};
324 buf.insert(buf.end(), ll, ll + 3);
326 uint8_t ll[] = {0x58,
static_cast<uint8_t
>(len & 0xff)};
327 buf.insert(buf.end(), ll, ll + 2);
331static inline void append_text_len(std::vector<uint8_t>& buf,
size_t len) {
332 check(len < 256,
"Text length too large");
334 buf.push_back(0x60 + len);
335 }
else if (len < 255) {
351static Nat compute_transcript_hash(
352 const uint8_t transcript[],
size_t len,
353 const std::vector<uint8_t>* docType =
nullptr) {
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',
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',
369 std::vector<uint8_t> deviceNameSpacesBytes = {0xD8, 0x18, 0x41, 0xA0};
371 if (docType !=
nullptr) {
372 docTypeBytes.clear();
373 append_text_len(docTypeBytes, docType->size());
374 docTypeBytes.insert(docTypeBytes.end(), docType->begin(), docType->end());
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());
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};
390 size_t l1 = da.size();
391 size_t l2 = l1 + (l1 < 256 ? 4 : 5);
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());
397 return nat_from_hash<Nat>(cose1.data(), cose1.size());
404template <
class Field>
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);
414template <
class Field>
415void fill_byte(std::vector<typename Field::Elt>& v, uint8_t b,
size_t i,
417 for (
size_t j = 0; j < 8; ++j) {
418 v[i*8 + j] = ( b >> j & 0x1 ) ? F.one() : F.zero();
422template <
class Field>
424 const Field &F,
size_t 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) {
431 std::vector<typename Field::Elt> v(96 * 8, F.of_scalar(0));
434 for (
size_t j = 0; j < attr.id_len && i < 96; ++j, ++i) {
435 fill_byte(v, attr.id[j], i, F);
437 fill_byte(v, 0x6C, i++, F);
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);
442 std::vector<uint8_t> vbuf;
443 uint8_t tag[] = {0xD9, 0x03, 0xEC, 0x6A};
445 case CborAttributeType::kPrimitive:
446 vbuf.push_back(attr.value[0]);
448 case CborAttributeType::kString:
449 append_text_len(vbuf, attr.value_len);
450 vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
452 case CborAttributeType::kBytes:
453 append_bytes_len(vbuf, attr.value_len);
454 vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
456 case CborAttributeType::kDate:
457 vbuf.insert(vbuf.end(), tag, tag + 4);
458 vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
460 case CborAttributeType::kInt:
461 vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
464 for (
size_t j = 0; j < vbuf.size() && i < 96; ++j, ++i) {
465 fill_byte(v, vbuf[j], i, F);
472template <
class EC,
class ScalarField>
473class MdocSignatureWitness {
474 using Field =
typename EC::Field;
476 using Nat =
typename Field::N;
486 EcdsaWitness ew_, dkw_;
487 MacWitnessF macs_[3];
489 explicit MdocSignatureWitness(
const EC& ec,
const ScalarField& Fn,
495 macs_{MacWitnessF(ec.f_, gf_), MacWitnessF(ec.f_, gf_),
496 MacWitnessF(ec.f_, gf_)} {}
499 filler.push_back(e_);
500 filler.push_back(dpkx_);
501 filler.push_back(dpky_);
503 ew_.fill_witness(filler);
504 dkw_.fill_witness(filler);
505 for (
auto& mac : macs_) {
506 mac.fill_witness(filler);
510 bool compute_witness(Elt pkX, Elt pkY,
const uint8_t mdoc[],
511 size_t len,
const uint8_t transcript[],
515 if (!pm.parse_device_response(len, mdoc)) {
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);
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);
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;
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);
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;
553 using vindex = std::array<Elt, kCborIndexBits>;
561 uint8_t signed_bytes_[kMaxSHABlocks * 64];
565 std::vector<std::vector<uint8_t>> attr_bytes_;
566 std::vector<std::vector<FlatSHA256Witness::BlockWitness>> atw_;
568 std::vector<uint8_t> attr_n_;
569 std::vector<CborIndex> attr_mso_;
570 std::vector<AttrShift> attr_ei_;
571 std::vector<AttrShift> attr_ev_;
579 explicit MdocHashWitness(
size_t num_attr,
const EC& ec,
const Field& Fn)
580 : ec_(ec), fn_(Fn), num_attr_(num_attr) {}
583 df.push_back(ind.k, kCborIndexBits, fn_);
587 df.push_back(attr.offset, kCborIndexBits, fn_);
588 df.push_back(attr.len, kCborIndexBits, fn_);
594 for (
size_t k = 0; k < 48; ++k) {
595 filler.push_back(BPENC.mkpacked_v32(bw.outw[k]));
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]));
601 for (
size_t k = 0; k < 8; ++k) {
602 filler.push_back(BPENC.mkpacked_v32(bw.h1[k]));
608 filler.push_back(numb_, 8, fn_);
610 for (
size_t i = kCose1PrefixLen; i < kMaxSHABlocks * 64; ++i) {
611 filler.push_back(signed_bytes_[i], 8, fn_);
613 for (
size_t j = 0; j < kMaxSHABlocks; j++) {
614 fill_sha(filler, bw_[j]);
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_);
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_);
628 for (
size_t j = 0; j < 2; j++) {
629 fill_sha(filler, atw_[ai][j]);
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]);
640 bool compute_witness(
const uint8_t mdoc[],
size_t len,
641 const uint8_t transcript[],
size_t tlen,
643 const uint8_t tnow[],
size_t version = 2) {
644 if (!pm_.parse_device_response(len, mdoc)) {
645 log(ERROR,
"Failed to parse device response");
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);
655 buf.assign(std::begin(kCose1Prefix), std::end(kCose1Prefix));
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]);
663 FlatSHA256Witness::transform_and_witness_message(
664 buf.size(), buf.data(), kMaxSHABlocks, numb_, signed_bytes_, bw_);
666 ECNat ne = nat_from_u32<ECNat>(bw_[numb_ - 1].h1);
667 e_ = ec_.f_.to_montgomery(ne);
669 memcpy(now_, tnow, 20);
671 size_t pmso = pm_.t_mso_.pos + 5;
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]));
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);
686 for (
size_t i = 0; i < attrs_len; ++i) {
687 attr_bytes_[i].resize(128);
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;
699 attr_ei_[i].len = fa.witness_length(attrs[i]);
701 attr_ev_[i].offset = fa.val_ind - fa.tag_ind;
702 attr_ev_[i].len = fa.val_len;
708 log(ERROR,
"Could not find attribute %.*s", attrs[i].id_len,
Definition bit_plucker_encoder.h:27
Definition host_decoder.h:47
Definition mac_witness.h:28
Definition mdoc_witness.h:91
Definition verify_witness.h:30
Definition mdoc_witness.h:48
Definition mdoc_witness.h:43
Definition flatsha256_witness.h:27
Definition mdoc_witness.h:55