// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_1F_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_1F_H_

#include <cstddef>
#include <cstdint>
#include <vector>

#include "circuits/cbor_parser/cbor.h"
#include "circuits/compiler/compiler.h"
#include "circuits/ecdsa/verify_circuit.h"
#include "circuits/logic/bit_plucker.h"
#include "circuits/logic/routing.h"
#include "circuits/mdoc/mdoc_1f_io.h"
#include "circuits/mdoc/mdoc_constants.h"
#include "circuits/sha/flatsha256_circuit.h"
namespace proofs {

template <class LogicCircuit, class Field, class EC, size_t kNumAttr>

;

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_1F_H_

class mdoc_1f {
  using EltW = typename LogicCircuit::EltW;
  using Elt = typename LogicCircuit::Elt;
  using Nat = typename Field::N;
  using Ecdsa = VerifyCircuit<LogicCircuit, Field, EC>;
  using EcdsaWitness = typename Ecdsa::Witness;
  using v8 = typename LogicCircuit::v8;
  using v32 = typename LogicCircuit::v32;
  using v256 = typename LogicCircuit::v256;
  using vind = typename LogicCircuit::template bitvec<kMdoc1CborIndexBits>;
  using Flatsha =
      FlatSHA256Circuit<LogicCircuit,
                        BitPlucker<LogicCircuit, kMdoc1SHAPluckerBits>>;
  using Routing = Routing<LogicCircuit>;
  using ShaBlockWitness = typename Flatsha::BlockWitness;
  using sha_packed_v32 = typename Flatsha::packed_v32;
  using Cbor = Cbor<LogicCircuit, kMdoc1CborIndexBits>;
  const LogicCircuit& lc_;
  const EC& ec_;
  const Nat& order_;
 public:
  struct CborIndex {
    vind k, v, ndx;
    void input(const LogicCircuit& lc) {
      k = lc.template vinput<kMdoc1CborIndexBits>();
      v = lc.template vinput<kMdoc1CborIndexBits>();
      ndx = lc.template vinput<kMdoc1CborIndexBits>();
    }
  };
  struct AttrShift {
    vind offset;
    vind len;
    void input(const LogicCircuit& lc) {
      offset = lc.template vinput<kMdoc1CborIndexBits>();
      len = lc.template vinput<kMdoc1CborIndexBits>();
    }
  };
  class Witness {
   public:
    EltW e_;
    EltW dpkx_, dpky_;
    EcdsaWitness sig_;
    EcdsaWitness dpk_sig_;
    v8 in_[64 * kMdoc1MaxSHABlocks];
    v8 nb_;
    ShaBlockWitness sig_sha_[kMdoc1MaxSHABlocks];
    size_t num_attr_;
    std::vector<std::vector<ShaBlockWitness>> attr_sha_;
    std::vector<std::vector<v8>> attrb_;
    std::vector<CborIndex> attr_mso_;
    std::vector<AttrShift> attr_ei_;
    std::vector<AttrShift> attr_ev_;
    std::vector<v8> incb_;
    std::vector<typename Cbor::position_witness> pwcb_;
    typename Cbor::global_witness gwcb_;
    vind prepad_, mso_len_;
    CborIndex valid_, valid_from_, valid_until_;
    CborIndex dev_key_info_, dev_key_, dev_key_pkx_, dev_key_pky_;
    CborIndex value_digests_, org_;
    explicit Witness(size_t num_attr)
        : num_attr_(num_attr),
          attr_sha_(num_attr),
          attrb_(num_attr),
          attr_mso_(num_attr),
          attr_ei_(num_attr),
          attr_ev_(num_attr),
          incb_(kMdoc1MaxMsoLen),
          pwcb_(kMdoc1MaxMsoLen) {
      for (size_t i = 0; i < num_attr; ++i) {
        attr_sha_[i].resize(2);
      }
    }
    void input(QuadCircuit<Field>& Q, const LogicCircuit& lc) {
      e_ = Q.input();
      dpkx_ = Q.input();
      dpky_ = Q.input();
      sig_.input(Q);
      dpk_sig_.input(Q);
      nb_ = lc.template vinput<8>();
      for (size_t i = 0; i + kCose1PrefixLen < 64 * kMdoc1MaxSHABlocks; ++i) {
        in_[i] = lc.template vinput<8>();
      }
      for (size_t j = 0; j < kMdoc1MaxSHABlocks; j++) {
        sig_sha_[j].input(Q);
      }
      prepad_ = lc.template vinput<kMdoc1CborIndexBits>();
      mso_len_ = lc.template vinput<kMdoc1CborIndexBits>();
      for (size_t i = 0; i < kMdoc1MaxMsoLen; ++i) {
        pwcb_[i].encoded_sel_header = Q.input();
      }
      gwcb_.invprod_decode = Q.input();
      gwcb_.cc0 = Q.input();
      gwcb_.invprod_parse = Q.input();
      valid_.input(lc);
      valid_from_.input(lc);
      valid_until_.input(lc);
      dev_key_info_.input(lc);
      dev_key_.input(lc);
      dev_key_pkx_.input(lc);
      dev_key_pky_.input(lc);
      value_digests_.input(lc);
      org_.input(lc);
      for (size_t ai = 0; ai < num_attr_; ++ai) {
        for (size_t i = 0; i < 64 * 2; ++i) {
          attrb_[ai].push_back(lc.template vinput<8>());
        }
        for (size_t j = 0; j < 2; j++) {
          attr_sha_[ai][j].input(Q);
        }
        attr_mso_[ai].input(lc);
        attr_ei_[ai].input(lc);
        attr_ev_[ai].input(lc);
      }
    }
  };
  struct OpenedAttribute {
    v8 attr[96];
  };
  struct PathEntry {
    CborIndex ind;
    size_t l;
    const uint8_t* name;
  };
  explicit mdoc_1f(const LogicCircuit& lc, const EC& ec, const Nat& order)
      : lc_(lc), ec_(ec), order_(order), sha_(lc), r_(lc), cbor_(lc) {}
  void assert_credential(EltW pkX, EltW pkY, EltW hash_tr,
                         OpenedAttribute oa[],
                         const v8 now[], const Witness& vw) const {
    Ecdsa ecc(lc_, ec_, order_);
    ecc.verify_signature3(pkX, pkY, vw.e_, vw.sig_);
    ecc.verify_signature3(vw.dpkx_, vw.dpky_, hash_tr, vw.dpk_sig_);
    sha_.assert_message_with_prefix(kMdoc1MaxSHABlocks, vw.nb_, vw.in_,
                                    kCose1Prefix, kCose1PrefixLen, vw.sig_sha_);
    assert_hash(vw.e_, vw);
    const v8 zz = lc_.template vbit<8>(0);
    std::vector<v8> cmp_buf(kMdoc1MaxMsoLen);
    std::vector<v8> in_cb(kMdoc1MaxMsoLen);
    r_.unshift(vw.prepad_, kMdoc1MaxMsoLen, in_cb.data(),
               kMdoc1MaxMsoLen - 5 - 2, vw.in_ + 5 + 2, zz, 3);
    std::vector<typename Cbor::decode> dsC(kMdoc1MaxMsoLen);
    std::vector<typename Cbor::parse_output> psC(kMdoc1MaxMsoLen);
    cbor_.decode_and_assert_decode_and_parse(kMdoc1MaxMsoLen, dsC.data(),
                                             psC.data(), in_cb.data(),
                                             vw.pwcb_.data(), vw.gwcb_);
    cbor_.assert_input_starts_at(kMdoc1MaxMsoLen, vw.prepad_, vw.mso_len_,
                                 dsC.data());
    PathEntry vk[2] = {{vw.valid_, kValidityInfoLen, kValidityInfoID},
                       {vw.valid_from_, kValidFromLen, kValidFromID}};
    assert_path(2, vk, vw, dsC, psC);
    cbor_.assert_date_before_at(kMdoc1MaxMsoLen, vw.valid_from_.v, now,
                                dsC.data());
    cbor_.assert_map_entry(kMdoc1MaxMsoLen, vw.valid_.v, 1, vw.valid_until_.k,
                           vw.valid_until_.v, vw.valid_until_.ndx, dsC.data(),
                           psC.data());
    cbor_.assert_text_at(kMdoc1MaxMsoLen, vw.valid_until_.k, kValidUntilLen,
                         kValidUntilID, dsC.data());
    cbor_.assert_date_after_at(kMdoc1MaxMsoLen, vw.valid_until_.v, now,
                               dsC.data());
    PathEntry dk[2] = {{vw.dev_key_info_, kDeviceKeyInfoLen, kDeviceKeyInfoID},
                       {vw.dev_key_, kDeviceKeyLen, kDeviceKeyID}};
    assert_path(2, dk, vw, dsC, psC);
    cbor_.assert_map_entry(kMdoc1MaxMsoLen, vw.dev_key_.v, 2, vw.dev_key_pkx_.k,
                           vw.dev_key_pkx_.v, vw.dev_key_pkx_.ndx, dsC.data(),
                           psC.data());
    cbor_.assert_map_entry(kMdoc1MaxMsoLen, vw.dev_key_.v, 2, vw.dev_key_pky_.k,
                           vw.dev_key_pky_.v, vw.dev_key_pky_.ndx, dsC.data(),
                           psC.data());
    cbor_.assert_negative_at(kMdoc1MaxMsoLen, vw.dev_key_pkx_.k, 1, dsC.data());
    cbor_.assert_negative_at(kMdoc1MaxMsoLen, vw.dev_key_pky_.k, 2, dsC.data());
    cbor_.assert_elt_as_be_bytes_at(kMdoc1MaxMsoLen, vw.dev_key_pkx_.v, 32,
                                    vw.dpkx_, dsC.data());
    cbor_.assert_elt_as_be_bytes_at(kMdoc1MaxMsoLen, vw.dev_key_pky_.v, 32,
                                    vw.dpky_, dsC.data());
    PathEntry ak[2] = {{vw.value_digests_, kValueDigestsLen, kValueDigestsID},
                       {vw.org_, kOrgLen, kOrgID}};
    assert_path(2, ak, vw, dsC, psC);
    for (size_t ai = 0; ai < vw.num_attr_; ++ai) {
      auto two = lc_.template vbit<8>(2);
      v8 B[96];
      sha_.assert_message(2, two, vw.attrb_[ai].data(),
                          vw.attr_sha_[ai].data());
      EltW h = repack32(vw.attr_sha_[ai][1].h1);
      cbor_.assert_map_entry(kMdoc1MaxMsoLen, vw.org_.v, 2, vw.attr_mso_[ai].k,
                             vw.attr_mso_[ai].v, vw.attr_mso_[ai].ndx,
                             dsC.data(), psC.data());
      cbor_.assert_elt_as_be_bytes_at(kMdoc1MaxMsoLen, vw.attr_mso_[ai].v, 32,
                                      h, dsC.data());
      r_.shift(vw.attr_ei_[ai].offset, 96, B, 128, vw.attrb_[ai].data(), zz, 3);
      assert_attribute(96, vw.attr_ei_[ai].len, B, oa[ai].attr);
    }
  }
 private:
  EltW repack32(const sha_packed_v32 H[]) const {
    EltW h = lc_.konst(0);
    Elt twok = lc_.one();
    for (size_t j = 8; j-- > 0;) {
      auto hj = sha_.bp_.unpack_v32(H[j]);
      for (size_t k = 0; k < 32; ++k) {
        h = lc_.axpy(&h, twok, lc_.eval(hj[k]));
        lc_.f_.add(twok, twok);
      }
    }
    return h;
  }
  void assert_hash(const EltW& e, const Witness& vw) const {
    sha_packed_v32 x[8];
    for (size_t b = 0; b < kMdoc1MaxSHABlocks; ++b) {
      auto bt = lc_.veq(vw.nb_, b + 1);
      auto ebt = lc_.eval(bt);
      for (size_t i = 0; i < 8; ++i) {
        for (size_t k = 0; k < sha_.bp_.kNv32Elts; ++k) {
          if (b == 0) {
            x[i][k] = lc_.mul(&ebt, vw.sig_sha_[b].h1[i][k]);
          } else {
            auto maybe_sha = lc_.mul(&ebt, vw.sig_sha_[b].h1[i][k]);
            x[i][k] = lc_.add(&x[i][k], maybe_sha);
          }
        }
      }
    }
    EltW h = repack32(x);
    lc_.assert_eq(&h, e);
  }
  void assert_attribute(size_t max, const vind& len, const v8 got[],
                        const v8 want[]) const {
    for (size_t j = 0; j < max; ++j) {
      auto ll = lc_.vlt(j, len);
      auto same = lc_.eq(8, got[j].data(), want[j].data());
      lc_.assert_implies(&ll, same);
    }
  }
  void assert_path(size_t len, PathEntry p[], const Witness& vw,
                   std::vector<typename Cbor::decode>& dsC,
                   std::vector<typename Cbor::parse_output>& psC) const {
    vind start = vw.prepad_;
    for (size_t i = 0; i < len; ++i) {
      cbor_.assert_map_entry(kMdoc1MaxMsoLen, start, i, p[i].ind.k, p[i].ind.v,
                             p[i].ind.ndx, dsC.data(), psC.data());
      cbor_.assert_text_at(kMdoc1MaxMsoLen, p[i].ind.k, p[i].l, p[i].name,
                           dsC.data());
      start = p[i].ind.v;
    }
  }
  Flatsha sha_;
  Routing r_;
  Cbor cbor_;
}

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_HASH_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_HASH_H_

#include <cstddef>
#include <cstdint>
#include <vector>

#include "circuits/compiler/compiler.h"
#include "circuits/logic/bit_plucker.h"
#include "circuits/logic/memcmp.h"
#include "circuits/logic/routing.h"
#include "circuits/mdoc/mdoc_constants.h"
#include "circuits/sha/flatsha256_circuit.h"

namespace proofs {

static constexpr size_t kSHAPluckerBits = 4u;

// This class (only) verifies the hashing and pseudo-parsing of an mdoc.
// Specifically, it checks
//   (a) the hash of the mdoc matches its mac.
//   (b) the values dpk_{x,y} appear in approximate cbor form, and their macs
//       match the expected values
//   (c) validFrom, validUntil appear in approximate cbor form, and that
//         validFrom <= now <= validUntil
//   (d) For each expected attribute, there exists a preimage to a sha hash
//       that appears in the mso, the preimage is approximately cbor formatted,
//       and the preimage includes the expected attribute id and value.
template <class LogicCircuit, class Field>
class MdocHash {
  using v8 = typename LogicCircuit::v8;
  using v32 = typename LogicCircuit::v32;
  using v256 = typename LogicCircuit::v256;

  using vind = typename LogicCircuit::template bitvec<kCborIndexBits>;

  using Flatsha = FlatSHA256Circuit<LogicCircuit,
                                    BitPlucker<LogicCircuit, kSHAPluckerBits>>;
  using ShaBlockWitness = typename Flatsha::BlockWitness;
  using sha_packed_v32 = typename Flatsha::packed_v32;

 public:
  // These structures mimic the similarly named structures in Witness, but
  // their members are circuit wire objects instead of size_t.
  struct OpenedAttribute {
    v8 attr[32]; /* 32b representing attribute name in be. */
    v8 v1[64];   /* 64b of attribute value */
  };
  struct CborIndex {
    vind k;
    void input(const LogicCircuit& lc) {
      k = lc.template vinput<kCborIndexBits>();
    }
  };

  struct AttrShift {
    vind offset;
    vind len;
    void input(const LogicCircuit& lc) {
      offset = lc.template vinput<kCborIndexBits>();
      len = lc.template vinput<kCborIndexBits>();
    }
  };

  class Witness {
   public:
    v8 in_[64 * kMaxSHABlocks]; /* input bytes, 64 * MAX */

    v8 nb_; /* index of sha block that contains the real hash  */
    ShaBlockWitness sig_sha_[kMaxSHABlocks];

    std::vector<std::vector<ShaBlockWitness>> attr_sha_;
    std::vector<std::vector<v8>> attrb_;

    CborIndex valid_from_, valid_until_;
    CborIndex dev_key_info_;
    CborIndex value_digests_;
    std::vector<CborIndex> attr_mso_;
    std::vector<AttrShift> attr_ei_;
    std::vector<AttrShift> attr_ev_;
    size_t num_attr_;

    explicit Witness(size_t num_attr) {
      num_attr_ = num_attr;
      attr_mso_.resize(num_attr);
      attr_ei_.resize(num_attr);
      attr_ev_.resize(num_attr);
      attr_sha_.resize(num_attr);
      for (size_t i = 0; i < num_attr; ++i) {
        attr_sha_[i].resize(2);
      }

      attrb_.resize(num_attr);
    }

    void input(QuadCircuit<Field>& Q, const LogicCircuit& lc) {
      nb_ = lc.template vinput<8>();

      // sha input init =========================
      for (size_t i = 0; i + kCose1PrefixLen < 64 * kMaxSHABlocks; ++i) {
        in_[i] = lc.template vinput<8>();
      }
      for (size_t j = 0; j < kMaxSHABlocks; j++) {
        sig_sha_[j].input(Q);
      }

      valid_from_.input(lc);
      valid_until_.input(lc);
      dev_key_info_.input(lc);
      value_digests_.input(lc);

      // // Attribute opening witnesses
      for (size_t ai = 0; ai < num_attr_; ++ai) {
        for (size_t i = 0; i < 64 * 2; ++i) {
          attrb_[ai].push_back(lc.template vinput<8>());
        }
        for (size_t j = 0; j < 2; j++) {
          attr_sha_[ai][j].input(Q);
        }
        attr_mso_[ai].input(lc);
        attr_ei_[ai].input(lc);
        attr_ev_[ai].input(lc);
      }
    }
  };

  explicit MdocHash(const LogicCircuit& lc) : lc_(lc), sha_(lc), r_(lc) {}

  void assert_valid_hash_mdoc(OpenedAttribute oa[/* NUM_ATTR */],
                              const v8 now[/*20*/], const v256& e,
                              const v256& dpkx, const v256& dpky,
                              const Witness& vw) const {
    sha_.assert_message_hash_with_prefix(kMaxSHABlocks, vw.nb_, vw.in_,
                                         kCose1Prefix, kCose1PrefixLen, e,
                                         vw.sig_sha_);

    // Shift a portion of the MSO into buf and check it.
    const v8 zz = lc_.template vbit<8>(0);  // cannot appear in strings
    std::vector<v8> cmp_buf(kMaxMsoLen);
    const Memcmp<LogicCircuit> CMP(lc_);

    // In the shifting below, the +5 corresponds to the prefix
    // D8 18 <len2> prefix of the mso that we want to skip parsing.
    // The +2 corresponds to the length.

    // validFrom <= now
    r_.shift(vw.valid_from_.k, kValidFromLen + kDateLen, &cmp_buf[0],
             kMaxMsoLen, vw.in_ + 5 + 2, zz, /*unroll=*/3);
    assert_bytes_at(kValidFromLen, &cmp_buf[0], kValidFromCheck);
    auto cmp = CMP.leq(kDateLen, &cmp_buf[kValidFromLen], &now[0]);
    lc_.assert1(cmp);

    // now <= validUntil
    r_.shift(vw.valid_until_.k, kValidUntilLen + kDateLen, &cmp_buf[0],
             kMaxMsoLen, vw.in_ + 5 + 2, zz, /*unroll=*/3);
    assert_bytes_at(kValidUntilLen, &cmp_buf[0], kValidUntilCheck);
    cmp = CMP.leq(kDateLen, &now[0], &cmp_buf[kValidUntilLen]);
    lc_.assert1(cmp);

    // DPK_{x,y}
    r_.shift(vw.dev_key_info_.k, kDeviceKeyInfoLen + 3 + 32 + 32, &cmp_buf[0],
             kMaxMsoLen, vw.in_ + 5 + 2, zz, /*unroll=*/3);
    assert_bytes_at(kDeviceKeyInfoLen, &cmp_buf[0], kDeviceKeyInfoCheck);
    uint8_t dpkyCheck[] = {0x22, 0x58, 0x20};
    assert_bytes_at(sizeof(dpkyCheck), &cmp_buf[65], dpkyCheck);

    assert_key(dpkx, &cmp_buf[kPkxInd]);
    assert_key(dpky, &cmp_buf[kPkyInd]);

    // Attributes parsing
    // valueDigests, ignore byte 13 \in {A1,A2} representing map size.
    r_.shift(vw.value_digests_.k, kValueDigestsLen, cmp_buf.data(), kMaxMsoLen,
             vw.in_ + 5 + 2, zz, /*unroll=*/3);
    assert_bytes_at(13, &cmp_buf[0], kValueDigestsCheck);
    assert_bytes_at(18, &cmp_buf[14], &kValueDigestsCheck[14]);

    // Attributes: Equality of hash with MSO value
    for (size_t ai = 0; ai < vw.num_attr_; ++ai) {
      v8 B[96];
      // Check the hash matches the value in the signed MSO.
      r_.shift(vw.attr_mso_[ai].k, 2 + 32, &cmp_buf[0], kMaxMsoLen,
               vw.in_ + 5 + 2, zz, /*unroll=*/3);

      // Basic CBOR check of the Tag
      assert_bytes_at(2, &cmp_buf[0], kTag32);

      v256 mm;
      // The loop below accounts for endian and v256 vs v8 types.
      for (size_t j = 0; j < 256; ++j) {
        mm[j] = cmp_buf[2 + (255 - j) / 8][(j % 8)];
      }

      auto two = lc_.template vbit<8>(2);
      sha_.assert_message_hash(2, two, vw.attrb_[ai].data(), mm,
                               vw.attr_sha_[ai].data());

      // Check that the attribute_id and value occur in the hashed text.
      r_.shift(vw.attr_ei_[ai].offset, 96, B, 128, vw.attrb_[ai].data(), zz, 3);
      assert_attribute(96, vw.attr_ei_[ai].len, B, oa[ai]);
    }
  }

 private:
  void assert_bytes_at(size_t len, const v8 buf[/*>=len*/],
                       const uint8_t want[/*len*/]) const {
    for (size_t i = 0; i < len; ++i) {
      auto want_i = lc_.template vbit<8>(want[i]);
      lc_.vassert_eq(&buf[i], want_i);
    }
  }

  // Checks that an attribute id or attribute value is as expected.
  // The len parameter holds the byte length of the expected id or value.
  void assert_attribute(size_t max, const vind& len, const v8 got[/*max*/],
                        const OpenedAttribute& oa) const {
    // Copy the attribute id and value into a single array.
    v8 want[96];
    for (size_t j = 0; j < 32; ++j) {
      want[j] = oa.attr[j];
    }
    for (size_t j = 0; j < 64; ++j) {
      want[32 + j] = oa.v1[j];
    }

    // Perform an equality check on the first len bytes.
    for (size_t j = 0; j < max; ++j) {
      auto ll = lc_.vlt(j, len);
      auto same = lc_.eq(8, got[j].data(), want[j].data());
      lc_.assert_implies(&ll, same);
    }
  }

  // Asserts that the key is equal to the value in big-endian order in buf_be.
  void assert_key(v256 key, const v8 buf_be[/*32*/]) const {
    v256 m;
    for (size_t i = 0; i < 256; ++i) {
      m[i] = buf_be[31 - (i / 8)][i % 8];
    }
    lc_.vassert_eq(&m, key);
  }

  // The constants below define the prefix of each field that is verified
  // in the MDOC. This string matching approach is substantially faster than
  // parsing the MDOC into cbor, and its soundness analysis provides at least 96
  // bits of static security.  These constants differ from similarly named ones
  // in mdoc_constants because they include header bytes; the mdoc_constants
  // values are used for cbor parsing of the raw mdoc, whereas these are used by
  // the circuit.

  // 69 [text(9)] 76616C696446726F6D [validFrom] C0 [tag(0)] 74 [len 20]
  static constexpr uint8_t kValidFromCheck[] = {
      0x69, 0x76, 0x61, 0x6C, 0x69, 0x64, 0x46, 0x72, 0x6F, 0x6D, 0xC0, 0x74};
  static constexpr size_t kValidFromLen = sizeof(kValidFromCheck);

  // 6A [text(10)] 76616C6964556E74696C [validUntil] C0 [tag(0)] 74
  static constexpr uint8_t kValidUntilCheck[] = {0x6A, 0x76, 0x61, 0x6C, 0x69,
                                                 0x64, 0x55, 0x6E, 0x74, 0x69,
                                                 0x6C, 0xC0, 0x74};
  static constexpr size_t kValidUntilLen = sizeof(kValidUntilCheck);

  // 6D text(13) 6465766963654B6579496E666F "deviceKeyInfo"
  //   A1 map(1) 69 text(9) 6465766963654B6579 "deviceKey"
  //      A4 map(4) 01 02 20 01
  //      21 negative(1) 58 20 bytes(32)
  //      <dpkx>
  //      22 negative(2) 58 20 bytes(32)
  //      <dpky>
  static constexpr uint8_t kDeviceKeyInfoCheck[] = {
      0x6D, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4B, 0x65, 0x79, 0x49,
      0x6E, 0x66, 0x6F, 0xA1, 0x69, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
      0x4B, 0x65, 0x79, 0xA4, 0x01, 0x02, 0x20, 0x01, 0x21, 0x58, 0x20};
  static constexpr size_t kDeviceKeyInfoLen = sizeof(kDeviceKeyInfoCheck);
  static constexpr size_t kPkxInd = kDeviceKeyInfoLen;
  static constexpr size_t kPkyInd = 68; /* 64 + 3 byte tag + 1*/

  // 6C text(12) 76616C756544696765737473  "valueDigests" A{1,2} # map(1,2)
  //     71 text(17) 6F72672E69736F2E31383031332E352E31 "org.iso.18013.5.1"
  static constexpr uint8_t kValueDigestsCheck[] = {
      0x6C, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x44, 0x69, 0x67,
      0x65, 0x73, 0x74, 0x73,
      0xA0,  // either {A1, A2}
      0x71, 0x6F, 0x72, 0x67, 0x2E, 0x69, 0x73, 0x6F, 0x2E,
      0x31, 0x38, 0x30, 0x31, 0x33, 0x2E, 0x35, 0x2E, 0x31};
  static constexpr size_t kValueDigestsLen = sizeof(kValueDigestsCheck);

  static constexpr size_t kDateLen = 20;

  const LogicCircuit& lc_;
  Flatsha sha_;
  Routing<LogicCircuit> r_;
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_HASH_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_1F_WITNESS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_1F_WITNESS_H_

#include <cstddef>
#include <cstdint>
#include <cstring>
#include <vector>

#include "arrays/dense.h"
#include "circuits/cbor_parser/cbor_witness.h"
#include "circuits/ecdsa/verify_witness.h"
#include "circuits/logic/bit_plucker_encoder.h"
#include "circuits/mdoc/mdoc_1f_io.h"
#include "circuits/mdoc/mdoc_constants.h"
#include "circuits/mdoc/mdoc_witness.h"
#include "circuits/mdoc/mdoc_zk.h"
#include "circuits/sha/flatsha256_witness.h"
#include "util/log.h"
namespace proofs {

template <typename EC, typename Field, class ScalarField>
class mdoc_1f_witness {
  using ECField = typename EC::Field;
  using ECElt = typename ECField::Elt;
  using ECNat = typename ECField::N;
  using Elt = typename Field::Elt;
  using Nat = typename Field::N;
  using EcdsaWitness = VerifyWitness3<EC, ScalarField>;
  using CborWitness = CborWitness<Field>;

 public:
  const EC ec_;
  Elt e_, e2_;      /* Issuer signature values. */
  Elt dpkx_, dpky_; /* device key */
  EcdsaWitness ew_, dkw_;
  uint8_t now_[kMdoc1DateLen]; /* CBOR-formatted time for expiry comparison. */

  FlatSHA256Witness::BlockWitness bw_[kMdoc1MaxSHABlocks];
  uint8_t signed_bytes_[kMdoc1MaxSHABlocks * 64];
  uint8_t numb_; /* Number of the correct sha block. */
  ParsedMdoc pm_;

  size_t num_attr_;
  std::vector<std::vector<uint8_t>> attr_bytes_;
  std::vector<std::vector<FlatSHA256Witness::BlockWitness>> atw_;

  std::vector<uint8_t> attr_n_; /* All attributes currently require 2 SHA. */
  std::vector<CborIndex> attr_mso_; /* The cbor indices of the attributes. */
  std::vector<AttrShift> attr_ei_;
  std::vector<AttrShift> attr_ev_;

  // Cbor parsing witnesses
  std::vector<typename CborWitness::v8> incb_;
  std::vector<typename CborWitness::position_witness> pwcb_;
  typename CborWitness::global_witness gwcb_;

  explicit mdoc_1f_witness(size_t num_attr, const EC& ec, const ScalarField& Fn)
      : ec_(ec),
        ew_(Fn, ec),
        dkw_(Fn, ec),
        num_attr_(num_attr),
        attr_bytes_(num_attr_),
        atw_(num_attr_),
        attr_n_(num_attr_),
        attr_mso_(num_attr_),
        attr_ei_(num_attr_),
        attr_ev_(num_attr_),
        incb_(kMdoc1MaxMsoLen),
        pwcb_(kMdoc1MaxMsoLen) {}

  void fill_sha(DenseFiller<Field>& filler,
                const FlatSHA256Witness::BlockWitness& bw) const {
    BitPluckerEncoder<Field, kMdoc1SHAPluckerBits> BPENC(ec_.f_);
    for (size_t k = 0; k < 48; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.outw[k]));
    }
    for (size_t k = 0; k < 64; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.oute[k]));
      filler.push_back(BPENC.mkpacked_v32(bw.outa[k]));
    }
    for (size_t k = 0; k < 8; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.h1[k]));
    }
  }

  void fill_attr_shift(DenseFiller<Field>& df, const AttrShift& attr) const {
    df.push_back(attr.offset, kMdoc1CborIndexBits, ec_.f_);
    df.push_back(attr.len, kMdoc1CborIndexBits, ec_.f_);
  }

  // The cbor index that is computed by our witness maker is with reference
  // to the beginning of the cbor string. However the convention for the cbor
  // parser is to 0-pad from the left to fill the full cbor string buffer.
  // As a result, all cbor indices need to be offset by the padding length.
  void fill_cbor_index(DenseFiller<Field>& filler, const CborIndex& ind,
                       size_t padding_offset = 0) const {
    filler.push_back(ind.k + padding_offset, kMdoc1CborIndexBits, ec_.f_);
    filler.push_back(ind.v + padding_offset, kMdoc1CborIndexBits, ec_.f_);
    filler.push_back(ind.ndx, kMdoc1CborIndexBits, ec_.f_);
  }

  void fill_witness(DenseFiller<Field>& filler, bool small = false) const {
    filler.push_back(e_);
    filler.push_back(dpkx_);
    filler.push_back(dpky_);

    ew_.fill_witness(filler);
    dkw_.fill_witness(filler);

    filler.push_back(numb_, 8, ec_.f_);
    for (size_t i = kCose1PrefixLen; i < kMdoc1MaxSHABlocks * 64; ++i) {
      filler.push_back(signed_bytes_[i], 8, ec_.f_);
    }
    for (size_t j = 0; j < kMdoc1MaxSHABlocks; j++) {
      fill_sha(filler, bw_[j]);
    }

    size_t prepad = kMdoc1MaxMsoLen - pm_.t_mso_.len + 5;
    filler.push_back(prepad, kMdoc1CborIndexBits, ec_.f_);
    filler.push_back(pm_.t_mso_.len - 5, kMdoc1CborIndexBits, ec_.f_);
    for (size_t i = 0; i < kMdoc1MaxMsoLen; ++i) {
      filler.push_back(pwcb_[i].encoded_sel_header);
    }
    filler.push_back(gwcb_.invprod_decode);
    filler.push_back(gwcb_.cc0);
    filler.push_back(gwcb_.invprod_parse);

    fill_cbor_index(filler, pm_.valid_, prepad);
    fill_cbor_index(filler, pm_.valid_from_, prepad);
    fill_cbor_index(filler, pm_.valid_until_, prepad);
    fill_cbor_index(filler, pm_.dev_key_info_, prepad);
    fill_cbor_index(filler, pm_.dev_key_, prepad);
    fill_cbor_index(filler, pm_.dev_key_pkx_, prepad);
    fill_cbor_index(filler, pm_.dev_key_pky_, prepad);
    fill_cbor_index(filler, pm_.value_digests_, prepad);
    fill_cbor_index(filler, pm_.org_, prepad);

    // Fill all attribute witnesses.
    for (size_t ai = 0; ai < num_attr_; ++ai) {
      for (size_t i = 0; i < 2 * 64; ++i) {
        filler.push_back(attr_bytes_[ai][i], 8, ec_.f_);
      }
      for (size_t j = 0; j < 2; j++) {
        fill_sha(filler, atw_[ai][j]);
      }

      // In the case of attribute mso, push the value to avoid having to
      // deal with 1- or 2- byte key length.
      // fill_cbor_index(filler, pm_.value_digests_);
      fill_cbor_index(filler, attr_mso_[ai], prepad);
      fill_attr_shift(filler, attr_ei_[ai]);
      fill_attr_shift(filler, attr_ev_[ai]);
    }
  }

  bool compute_witness(Elt pkX, Elt pkY, const uint8_t mdoc[/* len */],
                       size_t len, const uint8_t transcript[/* tlen */],
                       size_t tlen, const uint8_t tnow[/*kMdoc1DateLen*/],
                       const RequestedAttribute attrs[], size_t attrs_len) {
    if (!pm_.parse_device_response(len, mdoc)) {
      return false;
    }
    if (pm_.t_mso_.len >= kMdoc1MaxSHABlocks * 64 - 9 - kCose1PrefixLen) {
      log(ERROR, "tagged mso is too big: %zu", pm_.t_mso_.len);
      return false;
    }

    Nat ne = nat_from_hash<Nat>(pm_.tagged_mso_bytes_.data(),
                                pm_.tagged_mso_bytes_.size());
    e_ = ec_.f_.to_montgomery(ne);

    // Parse (r,s).
    const size_t l = pm_.sig_.len;
    Nat nr = nat_from_be<Nat>(&mdoc[pm_.sig_.pos]);
    Nat ns = nat_from_be<Nat>(&mdoc[pm_.sig_.pos + l / 2]);
    ew_.compute_witness(pkX, pkY, ne, nr, ns);

    Nat ne2 = compute_transcript_hash<Nat>(transcript, tlen, &pm_.doc_type_);
    const size_t l2 = pm_.dksig_.len;
    Nat nr2 = nat_from_be<Nat>(&mdoc[pm_.dksig_.pos]);
    Nat ns2 = nat_from_be<Nat>(&mdoc[pm_.dksig_.pos + l2 / 2]);
    size_t pmso = pm_.t_mso_.pos + 5; /* skip the tag */
    dpkx_ = ec_.f_.to_montgomery(
        nat_from_be<Nat>(&mdoc[pmso + pm_.dev_key_pkx_.pos]));
    dpky_ = ec_.f_.to_montgomery(
        nat_from_be<Nat>(&mdoc[pmso + pm_.dev_key_pky_.pos]));
    e2_ = ec_.f_.to_montgomery(ne2);
    dkw_.compute_witness(dpkx_, dpky_, ne2, nr2, ns2);

    memcpy(now_, tnow, kMdoc1DateLen);
    std::vector<uint8_t> buf;

    buf.assign(std::begin(kCose1Prefix), std::end(kCose1Prefix));
    // Add 2-byte length
    buf.push_back((pm_.t_mso_.len >> 8) & 0xff);
    buf.push_back(pm_.t_mso_.len & 0xff);
    for (size_t i = 0; i < pm_.t_mso_.len; ++i) {
      buf.push_back(mdoc[pm_.t_mso_.pos + i]);
    }

    FlatSHA256Witness::transform_and_witness_message(
        buf.size(), buf.data(), kMdoc1MaxSHABlocks, numb_, signed_bytes_, bw_);

    // Cbor parsing.
    // The input is expected to be pre-padded with zeros.
    // The +5 corresponds to the D8 18 59 <len2> prefix.
    size_t prepad = kMdoc1MaxMsoLen - pm_.t_mso_.len + 5;
    // Pad with enough 0s.
    buf.erase(buf.begin(), buf.begin() + kCose1PrefixLen + 2 + 5);
    buf.insert(buf.begin(), prepad, 0);

    CborWitness cw(ec_.f_);
    cw.fill_witnesses(kMdoc1MaxMsoLen, pm_.t_mso_.len, buf.data(), incb_.data(),
                      pwcb_.data(), gwcb_);

    // initialize variables
    for (size_t i = 0; i < num_attr_; ++i) {
      attr_bytes_[i].resize(128);
      atw_[i].resize(2);
    }

    // Match the attributes with the witnesses from the deviceResponse.
    for (size_t i = 0; i < num_attr_; ++i) {
      bool found = false;
      for (auto fa : pm_.attributes_) {
        if (fa == attrs[i]) {
          FlatSHA256Witness::transform_and_witness_message(
              fa.tag_len, &fa.doc[fa.tag_ind], 2, attr_n_[i],
              &attr_bytes_[i][0], &atw_[i][0]);
          attr_mso_[i] = fa.mso;
          attr_ei_[i].offset = fa.id_ind - fa.tag_ind;
          attr_ei_[i].len = fa.witness_length(attrs[i]);
          attr_ev_[i].offset = fa.val_ind - fa.tag_ind;
          attr_ev_[i].len = fa.val_len;
          found = true;
          break;
        }
      }
      if (!found) {
        log(ERROR, "Could not find attribute %.*s", attrs[i].id_len,
            attrs[i].id);
        return false;
      }
    }
    return true;
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_1F_WITNESS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_PTRCRED_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_PTRCRED_H_
#include <cstddef>
#include <vector>

#include "circuits/anoncred/small_io.h"
#include "circuits/compiler/compiler.h"
#include "circuits/ecdsa/verify_circuit.h"
#include "circuits/logic/bit_plucker.h"
#include "circuits/logic/memcmp.h"
#include "circuits/logic/routing.h"
#include "circuits/sha/flatsha256_circuit.h"

namespace proofs {

// This class creates a circuit to verify the signatures in a "ptrcred".
// A ptr credential is a document formatted as:
//      n // 8 bits specifying the number of attributes
//      ind <table of n pointer, each of 10 bits>
//          each pointer references from the beginning of the document
//      A byte string containing keys and values.
//          A key consists of 3 bytes.
//          A value can be arbitrarily long.
template <class LogicCircuit, class Field, class EC, size_t kNumAttr>
class PtrCred {
  using EltW = typename LogicCircuit::EltW;
  using Elt = typename LogicCircuit::Elt;
  using Nat = typename Field::N;
  using Ecdsa = VerifyCircuit<LogicCircuit, Field, EC>;
  using EcdsaWitness = typename Ecdsa::Witness;

  using v8 = typename LogicCircuit::v8;
  using v32 = typename LogicCircuit::v32;
  static constexpr size_t kIndexBits = 5;
  static constexpr size_t kMaxSHABlocks = 3;
  static constexpr size_t kMaxMsoLen = kMaxSHABlocks * 64 - 9;

  using vind = typename LogicCircuit::template bitvec<kIndexBits>;
  using Flatsha = FlatSHA256Circuit<LogicCircuit, BitPlucker<LogicCircuit, 3>>;
  using RoutingL = Routing<LogicCircuit>;
  using ShaBlockWitness = typename Flatsha::BlockWitness;

  const LogicCircuit& lc_;
  const EC& ec_;
  const Nat& order_;

 public:
  class Witness {
   public:
    EltW e_;
    EltW dpkx_, dpky_;

    EcdsaWitness sig_;
    EcdsaWitness dpk_sig_;

    v8 in_[64 * kMaxSHABlocks]; /* input bytes, 64 * MAX */
    v8 nb_; /* index of sha block that contains the real hash  */
    ShaBlockWitness sig_sha_[kMaxSHABlocks];

    void input(QuadCircuit<Field>& Q, const LogicCircuit& lc) {
      e_ = Q.input();
      dpkx_ = Q.input();
      dpky_ = Q.input();

      sig_.input(Q);
      dpk_sig_.input(Q);

      nb_ = lc.template vinput<8>();

      // sha input init =========================
      for (size_t i = 0; i < 64 * kMaxSHABlocks; ++i) {
        in_[i] = lc.template vinput<8>();
      }
      for (size_t j = 0; j < kMaxSHABlocks; j++) {
        sig_sha_[j].input(Q);
      }
    }
  };

  struct OpenedAttribute {
    v8 ind;    /* index of attribute */
    v8 len;    /* length of attribute, 1--32 */
    v8 v1[32]; /* attribute value */
    void input(const LogicCircuit& lc) {
      ind = lc.template vinput<8>();
      len = lc.template vinput<8>();
      for (size_t j = 0; j < 32; ++j) {
        v1[j] = lc.template vinput<8>();
      }
    }
  };

  EltW repack(const v8 in[], size_t ind) const {
    EltW h = lc_.konst(0);
    EltW base = lc_.konst(0x2);
    for (size_t i = 0; i < 32; ++i) {
      for (size_t j = 0; j < 8; ++j) {
        auto t = lc_.mul(&h, base);
        auto tin = lc_.eval(in[ind + i][7 - j]);
        h = lc_.add(&tin, t);
      }
    }
    return h;
  }

  explicit PtrCred(const LogicCircuit& lc, const EC& ec, const Nat& order)
      : lc_(lc), ec_(ec), order_(order), sha_(lc), r_(lc) {}

  void assert_credential(EltW pkX, EltW pkY, EltW hash_tr,
                         OpenedAttribute oa[/* NUM_ATTR */],
                         const v8 now[/*kDateLen*/], const Witness& vw) const {
    Ecdsa ecc(lc_, ec_, order_);

    ecc.verify_signature3(pkX, pkY, vw.e_, vw.sig_);
    ecc.verify_signature3(vw.dpkx_, vw.dpky_, hash_tr, vw.dpk_sig_);
    sha_.assert_message(kMaxSHABlocks, vw.nb_, vw.in_, vw.sig_sha_);

    const Memcmp<LogicCircuit> CMP(lc_);
    // validFrom <= now
    lc_.assert1(CMP.leq(kDateLen, &vw.in_[84], &now[0]));

    // now <= validUntil
    lc_.assert1(CMP.leq(kDateLen, &now[0], &vw.in_[92]));

    // DPK_{x,y}
    EltW dpkx = repack(vw.in_, 100);
    EltW dpky = repack(vw.in_, 132);
    lc_.assert_eq(&dpkx, vw.dpkx_);
    lc_.assert_eq(&dpky, vw.dpky_);

    // Attributes parsing
    const v8 zz = lc_.template vbit<8>(0xff);  // cannot appear in strings
    std::vector<v8> cmp_buf(32);
    for (size_t ai = 0; ai < kNumAttr; ++ai) {
      r_.shift(oa[ai].ind, 32, &cmp_buf[0], kMaxMsoLen, vw.in_, zz, 3);
      assert_attribute(32, oa[ai].len, &cmp_buf[0], &oa[ai].v1[0]);
    }
  }

 private:
  // Checks that an attribute id or attribute value is as expected.
  // The len parameter holds the byte length of the expected id or value.
  void assert_attribute(size_t max, const v8& vlen, const v8 got[/*max*/],
                        const v8 want[/*max*/]) const {
    for (size_t j = 0; j < max; ++j) {
      auto ll = lc_.vlt(j, vlen);
      auto cmp = lc_.veq(got[j], want[j]);
      lc_.assert_implies(&ll, cmp);
    }
  }

  Flatsha sha_;
  RoutingL r_;
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_PTRCRED_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_WITNESS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_WITNESS_H_

#include <stddef.h>
#include <string.h>

#include <array>
#include <cstdint>
#include <iterator>
#include <vector>

#include "arrays/dense.h"
#include "cbor/host_decoder.h"
#include "circuits/ecdsa/verify_witness.h"
#include "circuits/logic/bit_plucker_encoder.h"
#include "circuits/mac/mac_witness.h"
#include "circuits/mdoc/mdoc_constants.h"
#include "circuits/mdoc/mdoc_hash.h"
#include "circuits/mdoc/mdoc_zk.h"
#include "circuits/sha/flatsha256_witness.h"
#include "gf2k/gf2_128.h"
#include "util/crypto.h"
#include "util/log.h"
#include "util/panic.h"

namespace proofs {

struct CborIndex {
  size_t k, v, ndx;
  size_t pos, len; /* optional fields for string/byte values */
};

struct AttrShift {
  size_t offset, len;
};

// This class represents an attribute that is parsed out of the deviceResponse
// data structure. It includes offsets into the original mdoc which can be used
// to construct SHA witnesses for disclosing the value of an attribute.
struct FullAttribute {
  // Offset and length of the attribute identifier and attribute value.
  size_t id_ind;
  size_t id_len;
  size_t val_ind;
  size_t val_len;

  // Index for this attribute among all attributes in the mdoc hash list.
  size_t digest_id;
  CborIndex mso;

  // Offset and length of the attribute tag.
  size_t tag_ind;
  size_t tag_len;

  // The original mdoc into which all offsets point.
  const uint8_t* doc;

  bool operator==(const RequestedAttribute& y) const {
    return y.id_len == id_len && memcmp(y.id, &doc[id_ind], id_len) == 0;
  }

  size_t witness_length(const RequestedAttribute&  attr) {
    size_t r = id_len + val_len + 1 + 12;
    if (attr.type == CborAttributeType::kDate) {
      r += 4;
    } else if (attr.type == CborAttributeType::kString ||
               attr.type == CborAttributeType::kBytes) {
      r += 1;
      if (val_len > 23) r++;
      if (val_len > 255) r++;
    }
    return r;
  }
};

class ParsedMdoc {
 public:
  // Various cbor indices/witnesses for intermediate structures.
  CborIndex t_mso_, sig_, dksig_;
  CborIndex valid_, valid_from_, valid_until_;
  CborIndex dev_key_info_, dev_key_, dev_key_pkx_, dev_key_pky_;
  CborIndex value_digests_, org_;
  std::vector<FullAttribute> attributes_;
  std::vector<uint8_t> doc_type_;

  // These are the exact bytes which produce the hash that is signed.
  std::vector<uint8_t> tagged_mso_bytes_;

  /*
    Parses a byte representation of the "DeviceResponse" string from a phone.
    This contains all of the information needed to respond to an mdoc verifier.
    This method attempts to construct a witness from this.

    8.3.2.1.2.2: first field is "version", 2nd optional field is "documents"
    [documents][0][issuerSigned][issuerAuth]{2} --> tagged mso
    [documents][0][issuerSigned][issuerAuth]{3} --> issuer sig
    [documents][0][issuerSigned][nameSpaces][ns][index-of-attr] --> enc attr
    [documents][0][deviceSigned][deviceAuth][deviceSignature][3] --> sig

    This method produces indices into doc as state.
  */
  bool parse_device_response(size_t len, const uint8_t resp[/* len */]) {
    size_t np = 0;
    // When this object falls out of scope, all parsing objects will be
    // garbage collected.
    CborDoc root;
    bool ok = root.decode(resp, len, np, 0);
    if (!ok) {
      log(ERROR, "Failed to decode root");
      return false;
    }

    size_t di;
    auto docs = root.lookup(resp, 9, (uint8_t*)"documents", di);
    if (docs == nullptr) return false;
    // Fields of Document are "docType", "issuerSigned", "deviceSigned", ?errors

    auto docs0 = docs[1].index(0);
    if (docs0 == nullptr) return false;

    auto dt = docs0[0].lookup(resp, 7, (uint8_t*)"docType", di);
    if (dt == nullptr) return false;
    doc_type_.insert(doc_type_.begin(), resp + dt[1].u_.string.pos,
                     resp + dt[1].u_.string.pos + dt[1].u_.string.len);

    // The returned docs0 is the map, so index at [0].
    auto is = docs0[0].lookup(resp, 12, (uint8_t*)"issuerSigned", di);
    if (is == nullptr) return false;

    auto ia = is[1].lookup(resp, 10, (uint8_t*)"issuerAuth", di);
    if (ia == nullptr) return false;

    auto tmso = ia[1].index(2);
    if (tmso == nullptr) return false;
    copy_header(t_mso_, tmso);
    auto nsig = ia[1].index(3);
    if (nsig == nullptr) return false;
    copy_header(sig_, nsig);

    auto ns = is[1].lookup(resp, 10, (uint8_t*)"nameSpaces", di);
    if (ns == nullptr) return false;

    // Find the attribute witness we need from here.
    // For now, we only support 1 namespace.
    auto mldns = ns[1].lookup(resp, 17, (uint8_t*)"org.iso.18013.5.1", di);
    if (mldns == nullptr) return false;
    size_t ai = 0;
    auto tattr = mldns[1].index(ai++);
    while (tattr != nullptr) {
      CborDoc er;
      // Decode the map in this tagged attribute.
      size_t pos = tattr->children_[0].u_.string.pos;
      size_t end = pos + tattr->children_[0].u_.string.len;
      if (!er.decode(resp, end, pos, 0)) {
        return false;
      }

      auto ei = er.lookup(resp, 17, (uint8_t*)"elementIdentifier", di);
      if (ei == nullptr) return false;
      auto ev = er.lookup(resp, 12, (uint8_t*)"elementValue", di);
      if (ev == nullptr) return false;
      auto digid = er.lookup(resp, 8, (uint8_t*)"digestID", di);
      if (digid == nullptr) return false;

      attributes_.push_back(
          (FullAttribute){ei[1].position(),
                          ei[1].length(),
                          ev[1].position(),
                          ev[1].length(),
                          static_cast<size_t>(digid[1].u_.u64), /* digest_id */
                          {0, 0, 0},          /* default mso_ind */
                          tattr->header_pos_, /* tag_ind */
                          tattr->children_[0].u_.string.len +
                              4, /* +4 for the D8 18 58 <> prefix */
                          resp});

      tattr = mldns[1].index(ai++);
    }

    auto ds = docs0[0].lookup(resp, 12, (uint8_t*)"deviceSigned", di);
    if (ds == nullptr) return false;
    auto da = ds[1].lookup(resp, 10, (uint8_t*)"deviceAuth", di);
    if (da == nullptr) return false;
    auto dsi = da[1].lookup(resp, 15, (uint8_t*)"deviceSignature", di);
    if (dsi == nullptr) return false;
    auto ndksig = dsi[1].index(3);
    if (ndksig == nullptr) return false;
    copy_header(dksig_, ndksig);

    // Then parse tagged mso. Skip 5 bytes to skip the D8 18 59 <len2>.
    const uint8_t* pmso = resp + tmso->u_.string.pos + 5;
    size_t pos = 0;
    CborDoc mso;
    if (!mso.decode(pmso, tmso->u_.string.len - 5, pos, 0)) return false;
    auto nv = mso.lookup(pmso, kValidityInfoLen, kValidityInfoID, valid_.ndx);
    if (nv == nullptr) return false;
    copy_kv_header(valid_, nv);

    auto nvf = nv[1].lookup(pmso, kValidFromLen, kValidFromID, valid_from_.ndx);
    if (nvf == nullptr) return false;
    copy_kv_header(valid_from_, nvf);

    auto nvu =
        nv[1].lookup(pmso, kValidUntilLen, kValidUntilID, valid_until_.ndx);
    if (nvu == nullptr) return false;
    copy_kv_header(valid_until_, nvu);

    auto ndki = mso.lookup(pmso, kDeviceKeyInfoLen, kDeviceKeyInfoID,
                           dev_key_info_.ndx);
    if (ndki == nullptr) return false;
    copy_kv_header(dev_key_info_, ndki);

    auto ndk = ndki[1].lookup(pmso, kDeviceKeyLen, kDeviceKeyID, dev_key_.ndx);
    if (ndk == nullptr) return false;
    copy_kv_header(dev_key_, ndk);

    auto npkx = ndk[1].lookup_negative(-1, dev_key_pkx_.ndx);
    if (npkx == nullptr) return false;
    copy_kv_header(dev_key_pkx_, npkx);

    auto npky = ndk[1].lookup_negative(-2, dev_key_pky_.ndx);
    if (npky == nullptr) return false;
    copy_kv_header(dev_key_pky_, npky);

    auto nvd =
        mso.lookup(pmso, kValueDigestsLen, kValueDigestsID, value_digests_.ndx);
    if (nvd == nullptr) return false;
    copy_kv_header(value_digests_, nvd);

    auto norg = nvd[1].lookup(pmso, kOrgLen, kOrgID, org_.ndx);
    if (norg == nullptr) return false;
    copy_kv_header(org_, norg);

    for (auto& attr : attributes_) {
      uint64_t hi = (uint64_t)attr.digest_id;
      auto hattr = norg[1].lookup_unsigned(hi, attr.mso.ndx);
      if (hattr == nullptr) return false;
      copy_kv_header(attr.mso, hattr);
    }

    tagged_mso_bytes_.assign(std::begin(kCose1Prefix), std::end(kCose1Prefix));
    // Add 2-byte length
    tagged_mso_bytes_.push_back((t_mso_.len >> 8) & 0xff);
    tagged_mso_bytes_.push_back(t_mso_.len & 0xff);
    for (size_t i = 0; i < t_mso_.len; ++i) {
      tagged_mso_bytes_.push_back(resp[t_mso_.pos + i]);
    }

    return true;
  }

 private:
  // Used to copy the results of a map lookup.
  static void copy_kv_header(CborIndex& ind, const CborDoc* n) {
    ind.k = n[0].header_pos_;
    ind.v = n[1].header_pos_;

    if (n[1].t_ == TEXT || n[1].t_ == BYTES) {
      ind.pos = n[1].u_.string.pos;
      ind.len = n[1].u_.string.len;
    }
  }

  // Used to copy the results of an index lookup.
  static void copy_header(CborIndex& ind, const CborDoc* n) {
    ind.k = n->header_pos_;
    ind.pos = n->u_.string.pos;
    ind.len = n->u_.string.len;
  }
};

// Transform from u8 be (i.e., be[31] is the most significant byte) into
// nat form, which requires first converting to le byte order.
template <class Nat>
Nat nat_from_be(const uint8_t be[/* Nat::kBytes */]) {
  uint8_t tmp[Nat::kBytes];
  // Transform into byte-wise le representation.
  for (size_t i = 0; i < Nat::kBytes; ++i) {
    tmp[i] = be[Nat::kBytes - i - 1];
  }
  return Nat::of_bytes(tmp);
}

// Transform from u32 be (i.e., be[0] is the most significant nibble)
// into nat form, which requires first converting to le byte order.
template <class Nat>
Nat nat_from_u32(const uint32_t be[]) {
  uint8_t tmp[Nat::kBytes];
  const size_t top = Nat::kBytes / 4;
  for (size_t i = 0; i < Nat::kBytes; ++i) {
    tmp[i] = (be[top - i / 4 - 1] >> ((i % 4) * 8)) & 0xff;
  }
  return Nat::of_bytes(tmp);
}

template <typename Nat>
Nat nat_from_hash(const uint8_t data[], size_t len) {
  uint8_t hash[kSHA256DigestSize];
  SHA256 sha;
  sha.Update(data, len);
  sha.DigestData(hash);
  Nat ne = nat_from_be<Nat>(hash);
  return ne;
}

static inline void append_bytes_len(std::vector<uint8_t>& buf, size_t len) {
  if (len > 256) {
    uint8_t ll[] = {0x59, (uint8_t)((len >> 8) & 0xff), (uint8_t)(len & 0xff)};
    buf.insert(buf.end(), ll, ll + 3);
  } else {
    uint8_t ll[] = {0x58, static_cast<uint8_t>(len & 0xff)};
    buf.insert(buf.end(), ll, ll + 2);
  }
}

static inline void append_text_len(std::vector<uint8_t>& buf, size_t len) {
  check(len < 256, "Text length too large");
  if (len < 24) {
    buf.push_back(0x60 + len);
  } else if (len < 255) {
    buf.push_back(0x78);
    buf.push_back(len);
  }
}

// Form the COSE1 encoding of the DeviceAuthenticationBytes,
// then compute its SHA-256 hash, and cast into a Nat.
// The original form follows S9.1.3.4 of the mdoc spec and
// assumed that the transcript was a simple string.
// The Jan'2024 demo requires using the "AndroidHandover" version
// of the DeviceAuthenticationBytes formatting, which is not
// specified in the spec. As a result, this function is a hack
// that mimics the bytes produced by the Android com.android.identity.wallet
// library.
template <class Nat>
static Nat compute_transcript_hash(
    const uint8_t transcript[], size_t len,
    const std::vector<uint8_t>* docType = nullptr) {
  // The DeviceAuthenticationBytes is defined in 9.1.3.4 as:
  // DeviceAuthentication = [
  //    "DeviceAuthentication",
  //    SessionTranscript,
  //    DocType, ; Same as in mdoc response
  //    DeviceNameSpacesBytes ; Same as in mdoc response
  // ]
  std::vector<uint8_t> deviceAuthentication = {
      0x84, 0x74, 'D', 'e', 'v', 'i', 'c', 'e', 'A', 'u', 't',
      'h',  'e',  'n', 't', 'i', 'c', 'a', 't', 'i', 'o', 'n',
  };
  std::vector<uint8_t> docTypeBytes = {
      0x75, 'o', 'r', 'g', '.', 'i', 's', 'o', '.', '1', '8',
      '0',  '1', '3', '.', '5', '.', '1', '.', 'm', 'D', 'L',
  };
  std::vector<uint8_t> deviceNameSpacesBytes = {0xD8, 0x18, 0x41, 0xA0};

  if (docType != nullptr) {
    docTypeBytes.clear();
    append_text_len(docTypeBytes, docType->size());
    docTypeBytes.insert(docTypeBytes.end(), docType->begin(), docType->end());
  }

  // Provide the DeviceAuthentication bytes
  std::vector<uint8_t> da(deviceAuthentication);
  da.insert(da.end(), transcript, transcript + len);
  da.insert(da.end(), docTypeBytes.begin(), docTypeBytes.end());
  da.insert(da.end(), deviceNameSpacesBytes.begin(),
            deviceNameSpacesBytes.end());

  // Form the COSE1 encoding of the DeviceAuthenticationBytes.
  std::vector<uint8_t> cose1{0x84, 0x6A, 0x53, 0x69, 0x67, 0x6E,
                             0x61, 0x74, 0x75, 0x72, 0x65, 0x31,
                             0x43, 0xA1, 0x01, 0x26, 0x40};
  uint8_t tag[] = {0xD8, 0x18};

  size_t l1 = da.size();
  size_t l2 = l1 + (l1 < 256 ? 4 : 5); /* Tagged array length. */
  append_bytes_len(cose1, l2);
  cose1.insert(cose1.end(), tag, tag + 2);
  append_bytes_len(cose1, l1);
  cose1.insert(cose1.end(), da.begin(), da.end());

  return nat_from_hash<Nat>(cose1.data(), cose1.size());
}

// Interpret input s as an len*8-bit string, and use it to fill max*8 bits
// in the dense filler.
// Pad the input with the Field value 2 to indicate the positions
// that are not part of the string.
template <class Field>
void fill_bit_string(DenseFiller<Field>& filler, const uint8_t s[/*len*/],
                     size_t len, size_t max, const Field& Fs) {
  std::vector<typename Field::Elt> v(max * 8, Fs.of_scalar(2));
  for (size_t i = 0; i < max && i < len; ++i) {
    fill_byte(v, s[i], i, Fs);
  }
  filler.push_back(v);
}

template <class Field>
void fill_byte(std::vector<typename Field::Elt>& v, uint8_t b, size_t i,
               const Field& F) {
  for (size_t j = 0; j < 8; ++j) {
    v[i*8 + j] = ( b >> j & 0x1 ) ? F.one() : F.zero();
  }
}

template <class Field>
void fill_attribute(DenseFiller<Field> &filler, const RequestedAttribute &attr,
                    const Field &F, size_t version = 2) {
  if (version <= 2) {
    fill_bit_string(filler, attr.id, attr.id_len, 32, F);
    fill_bit_string(filler, attr.value, attr.value_len, 64, F);
  } else if (version == 3) {
    // In version 3, the attribute is encoded as the raw cbor string that
    // included <name of identifier> <elementValue> <attributeValue>
    std::vector<typename Field::Elt> v(96 * 8, F.of_scalar(0));

    size_t i = 0;
    for (size_t j = 0; j < attr.id_len && i < 96; ++j, ++i) {
      fill_byte(v, attr.id[j], i, F);
    }
    fill_byte(v, 0x6C, i++, F);  // Cbor type for length 12 string.
    const char* ev = "elementValue";
    for (size_t j = 0; j < 12 && i < 96; ++j, ++i) {
      fill_byte(v, ev[j], i, F);
    }
    std::vector<uint8_t> vbuf;
    uint8_t tag[] = {0xD9, 0x03, 0xEC, 0x6A};
    switch (attr.type) {
      case CborAttributeType::kPrimitive:
        vbuf.push_back(attr.value[0]);
        break;
      case CborAttributeType::kString:
        append_text_len(vbuf, attr.value_len);
        vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
        break;
      case CborAttributeType::kBytes:
        append_bytes_len(vbuf, attr.value_len);
        vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
        break;
      case CborAttributeType::kDate:
        vbuf.insert(vbuf.end(), tag, tag + 4);
        vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
        break;
      case CborAttributeType::kInt:
        vbuf.insert(vbuf.end(), attr.value, attr.value + attr.value_len);
        break;
    }
    for (size_t j = 0; j < vbuf.size() && i < 96; ++j, ++i) {
      fill_byte(v, vbuf[j], i, F);
    }
    filler.push_back(v);
  }
}

template <class EC, class ScalarField>
class MdocSignatureWitness {
  using Field = typename EC::Field;
  using Elt = typename Field::Elt;
  using Nat = typename Field::N;
  using EcdsaWitness = VerifyWitness3<EC, ScalarField>;
  using MacWitnessF = MacWitness<Field>;
  using f_128 = GF2_128<>;
  const EC& ec_;
  const f_128& gf_;

 public:
  Elt e_, e2_;      /* Issuer signature values. */
  Elt dpkx_, dpky_; /* device key */
  EcdsaWitness ew_, dkw_;
  MacWitnessF macs_[3]; /* macs for e_, dpkx_, dpky_ */

  explicit MdocSignatureWitness(const EC& ec, const ScalarField& Fn,
                                const f_128& gf)
      : ec_(ec),
        gf_(gf),
        ew_(Fn, ec),
        dkw_(Fn, ec),
        macs_{MacWitnessF(ec.f_, gf_), MacWitnessF(ec.f_, gf_),
              MacWitnessF(ec.f_, gf_)} {}

  void fill_witness(DenseFiller<Field>& filler) const {
    filler.push_back(e_);
    filler.push_back(dpkx_);
    filler.push_back(dpky_);

    ew_.fill_witness(filler);
    dkw_.fill_witness(filler);
    for (auto& mac : macs_) {
      mac.fill_witness(filler);
    }
  }

  bool compute_witness(Elt pkX, Elt pkY, const uint8_t mdoc[/* len */],
                       size_t len, const uint8_t transcript[/* tlen */],
                       size_t tlen) {
    ParsedMdoc pm;

    if (!pm.parse_device_response(len, mdoc)) {
      return false;
    }

    Nat ne = nat_from_hash<Nat>(pm.tagged_mso_bytes_.data(),
                                pm.tagged_mso_bytes_.size());
    e_ = ec_.f_.to_montgomery(ne);

    // Parse (r,s).
    const size_t l = pm.sig_.len;
    Nat nr = nat_from_be<Nat>(&mdoc[pm.sig_.pos]);
    Nat ns = nat_from_be<Nat>(&mdoc[pm.sig_.pos + l / 2]);
    ew_.compute_witness(pkX, pkY, ne, nr, ns);

    Nat ne2 = compute_transcript_hash<Nat>(transcript, tlen, &pm.doc_type_);
    const size_t l2 = pm.dksig_.len;
    Nat nr2 = nat_from_be<Nat>(&mdoc[pm.dksig_.pos]);
    Nat ns2 = nat_from_be<Nat>(&mdoc[pm.dksig_.pos + l2 / 2]);
    size_t pmso = pm.t_mso_.pos + 5; /* skip the tag */
    dpkx_ = ec_.f_.to_montgomery(
        nat_from_be<Nat>(&mdoc[pmso + pm.dev_key_pkx_.pos]));
    dpky_ = ec_.f_.to_montgomery(
        nat_from_be<Nat>(&mdoc[pmso + pm.dev_key_pky_.pos]));
    e2_ = ec_.f_.to_montgomery(ne2);
    dkw_.compute_witness(dpkx_, dpky_, ne2, nr2, ns2);
    return true;
  }
};

// EC: implements the elliptic curve for the mdoc
// Field: implements the field used to define the sumcheck circuit, which can
//        be smaller than the EC field
template <typename EC, typename Field>
class MdocHashWitness {
  using ECField = typename EC::Field;
  using ECElt = typename ECField::Elt;
  using ECNat = typename ECField::N;
  using Elt = typename Field::Elt;
  using vindex = std::array<Elt, kCborIndexBits>;

  const EC& ec_;
  const Field& fn_;

 public:
  ECElt e_;           /* Issuer signature values. */
  ECElt dpkx_, dpky_; /* device key */
  uint8_t signed_bytes_[kMaxSHABlocks * 64];
  uint8_t numb_; /* Number of the correct sha block. */

  size_t num_attr_;
  std::vector<std::vector<uint8_t>> attr_bytes_;
  std::vector<std::vector<FlatSHA256Witness::BlockWitness>> atw_;

  std::vector<uint8_t> attr_n_; /* All attributes currently require 2 SHA. */
  std::vector<CborIndex> attr_mso_; /* The cbor indices of the attributes. */
  std::vector<AttrShift> attr_ei_;
  std::vector<AttrShift> attr_ev_;

  FlatSHA256Witness::BlockWitness bw_[kMaxSHABlocks];

  ParsedMdoc pm_;

  uint8_t now_[20]; /* CBOR-formatted time used for expiry comparison. */

  explicit MdocHashWitness(size_t num_attr, const EC& ec, const Field& Fn)
      : ec_(ec), fn_(Fn), num_attr_(num_attr) {}

  void fill_cbor_index(DenseFiller<Field>& df, const CborIndex& ind) const {
    df.push_back(ind.k, kCborIndexBits, fn_);
  }

  void fill_attr_shift(DenseFiller<Field>& df, const AttrShift& attr) const {
    df.push_back(attr.offset, kCborIndexBits, fn_);
    df.push_back(attr.len, kCborIndexBits, fn_);
  }

  void fill_sha(DenseFiller<Field>& filler,
                const FlatSHA256Witness::BlockWitness& bw) const {
    BitPluckerEncoder<Field, kSHAPluckerBits> BPENC(fn_);
    for (size_t k = 0; k < 48; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.outw[k]));
    }
    for (size_t k = 0; k < 64; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.oute[k]));
      filler.push_back(BPENC.mkpacked_v32(bw.outa[k]));
    }
    for (size_t k = 0; k < 8; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.h1[k]));
    }
  }

  void fill_witness(DenseFiller<Field>& filler) const {
    // Fill sha of main mso.
    filler.push_back(numb_, 8, fn_);
    // Don't push the prefix.
    for (size_t i = kCose1PrefixLen; i < kMaxSHABlocks * 64; ++i) {
      filler.push_back(signed_bytes_[i], 8, fn_);
    }
    for (size_t j = 0; j < kMaxSHABlocks; j++) {
      fill_sha(filler, bw_[j]);
    }
    // === done with sha

    fill_cbor_index(filler, pm_.valid_from_);
    fill_cbor_index(filler, pm_.valid_until_);
    fill_cbor_index(filler, pm_.dev_key_info_);
    fill_cbor_index(filler, pm_.value_digests_);

    // Fill all attribute witnesses.
    for (size_t ai = 0; ai < num_attr_; ++ai) {
      for (size_t i = 0; i < 2 * 64; ++i) {
        filler.push_back(attr_bytes_[ai][i], 8, fn_);
      }
      for (size_t j = 0; j < 2; j++) {
        fill_sha(filler, atw_[ai][j]);
      }

      // In the case of attribute mso, push the value to avoid having to
      // deal with 1- or 2- byte key length.
      filler.push_back(attr_mso_[ai].v, kCborIndexBits, fn_);
      fill_attr_shift(filler, attr_ei_[ai]);
      fill_attr_shift(filler, attr_ev_[ai]);
    }
  }

  bool compute_witness(const uint8_t mdoc[/* len */], size_t len,
                       const uint8_t transcript[/* tlen */], size_t tlen,
                       const RequestedAttribute attrs[], size_t attrs_len,
                       const uint8_t tnow[/*20*/], size_t version = 2) {
    if (!pm_.parse_device_response(len, mdoc)) {
      log(ERROR, "Failed to parse device response");
      return false;
    }

    std::vector<uint8_t> buf;
    if (pm_.t_mso_.len >= kMaxSHABlocks * 64 - 9 - kCose1PrefixLen) {
      log(ERROR, "tagged mso is too big: %zu", pm_.t_mso_.len);
      return false;
    }

    buf.assign(std::begin(kCose1Prefix), std::end(kCose1Prefix));
    // Add 2-byte length
    buf.push_back((pm_.t_mso_.len >> 8) & 0xff);
    buf.push_back(pm_.t_mso_.len & 0xff);
    for (size_t i = 0; i < pm_.t_mso_.len; ++i) {
      buf.push_back(mdoc[pm_.t_mso_.pos + i]);
    }

    FlatSHA256Witness::transform_and_witness_message(
        buf.size(), buf.data(), kMaxSHABlocks, numb_, signed_bytes_, bw_);

    ECNat ne = nat_from_u32<ECNat>(bw_[numb_ - 1].h1);
    e_ = ec_.f_.to_montgomery(ne);

    memcpy(now_, tnow, 20);

    size_t pmso = pm_.t_mso_.pos + 5; /* +5 to skip the tag */
    dpkx_ = ec_.f_.to_montgomery(
        nat_from_be<ECNat>(&mdoc[pmso + pm_.dev_key_pkx_.pos]));
    dpky_ = ec_.f_.to_montgomery(
        nat_from_be<ECNat>(&mdoc[pmso + pm_.dev_key_pky_.pos]));

    // initialize variables
    attr_n_.resize(attrs_len);
    attr_mso_.resize(attrs_len);
    attr_ev_.resize(attrs_len);
    attr_ei_.resize(attrs_len);
    attr_bytes_.resize(attrs_len);
    atw_.resize(attrs_len);

    // Match the attributes with the witnesses from the deviceResponse.
    for (size_t i = 0; i < attrs_len; ++i) {
      attr_bytes_[i].resize(128);
      atw_[i].resize(2);
      bool found = false;
      for (auto fa : pm_.attributes_) {
        if (fa == attrs[i]) {
          FlatSHA256Witness::transform_and_witness_message(
              fa.tag_len, &fa.doc[fa.tag_ind], 2, attr_n_[i],
              &attr_bytes_[i][0], &atw_[i][0]);
          attr_mso_[i] = fa.mso;
          attr_ei_[i].offset = fa.id_ind - fa.tag_ind;
          attr_ei_[i].len = fa.id_len;
          if (version > 2) {
            attr_ei_[i].len = fa.witness_length(attrs[i]);
          }
          attr_ev_[i].offset = fa.val_ind - fa.tag_ind;
          attr_ev_[i].len = fa.val_len;
          found = true;
          break;
        }
      }
      if (!found) {
        log(ERROR, "Could not find attribute %.*s", attrs[i].id_len,
            attrs[i].id);
        return false;
      }
    }
    return true;
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_WITNESS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_H_

#include <cstddef>
#include <vector>

#include "circuits/anoncred/small_io.h"
#include "circuits/compiler/compiler.h"
#include "circuits/ecdsa/verify_circuit.h"
#include "circuits/logic/bit_plucker.h"
#include "circuits/logic/memcmp.h"
#include "circuits/logic/routing.h"
#include "circuits/sha/flatsha256_circuit.h"

namespace proofs {

// This class creates a circuit to verify the signatures in a "small" MDOC.
// A small credential is a 183-byte document formatted as:
//      first_name    32 0
//      family_name   32 32
//      date_of_birth YYYYMMDD 64
//      gender        B 72
//      age_over_X.   BBBBBBB 73    [16, 18, 21, 25, 62, 65, 67]
//      issuerid   BBBB 80
//      validfrom  YYYYMMDD 84
//      validuntil YYYYMMDD 92
//      DPKX  32x 100
//      DPKY  32x 132
//      <arbitrary bytes of information>
template <class LogicCircuit, class Field, class EC, size_t kNumAttr>
class Small {
  using EltW = typename LogicCircuit::EltW;
  using Elt = typename LogicCircuit::Elt;
  using Nat = typename Field::N;
  using Ecdsa = VerifyCircuit<LogicCircuit, Field, EC>;
  using EcdsaWitness = typename Ecdsa::Witness;

  using v8 = typename LogicCircuit::v8;
  using v32 = typename LogicCircuit::v32;
  static constexpr size_t kIndexBits = 5;
  static constexpr size_t kMaxSHABlocks = 7;
  static constexpr size_t kMaxMsoLen = kMaxSHABlocks * 64 - 9;

  using vind = typename LogicCircuit::template bitvec<kIndexBits>;
  using Flatsha = FlatSHA256Circuit<LogicCircuit, BitPlucker<LogicCircuit, 3>>;
  using RoutingL = Routing<LogicCircuit>;
  using ShaBlockWitness = typename Flatsha::BlockWitness;

  const LogicCircuit& lc_;
  const EC& ec_;
  const Nat& order_;

 public:
  class Witness {
   public:
    EltW e_;
    EltW dpkx_, dpky_;

    EcdsaWitness sig_;
    EcdsaWitness dpk_sig_;

    v8 in_[64 * kMaxSHABlocks]; /* input bytes, 64 * MAX */
    v8 nb_; /* index of sha block that contains the real hash  */
    ShaBlockWitness sig_sha_[kMaxSHABlocks];

    void input(QuadCircuit<Field>& Q, const LogicCircuit& lc) {
      e_ = Q.input();
      dpkx_ = Q.input();
      dpky_ = Q.input();

      sig_.input(Q);
      dpk_sig_.input(Q);

      nb_ = lc.template vinput<8>();

      // sha input init =========================
      for (size_t i = 0; i < 64 * kMaxSHABlocks; ++i) {
        in_[i] = lc.template vinput<8>();
      }
      for (size_t j = 0; j < kMaxSHABlocks; j++) {
        sig_sha_[j].input(Q);
      }
    }
  };

  struct OpenedAttribute {
    v8 ind;    /* index of attribute */
    v8 len;    /* length of attribute, 1--32 */
    v8 v1[32]; /* attribute value */
    void input(const LogicCircuit& lc) {
      ind = lc.template vinput<8>();
      len = lc.template vinput<8>();
      for (size_t j = 0; j < 32; ++j) {
        v1[j] = lc.template vinput<8>();
      }
    }
  };

  EltW repack(const v8 in[], size_t ind) const {
    EltW h = lc_.konst(0);
    EltW base = lc_.konst(0x2);
    for (size_t i = 0; i < 32; ++i) {
      for (size_t j = 0; j < 8; ++j) {
        auto t = lc_.mul(&h, base);
        auto tin = lc_.eval(in[ind + i][7 - j]);
        h = lc_.add(&tin, t);
      }
    }
    return h;
  }

  explicit Small(const LogicCircuit& lc, const EC& ec, const Nat& order)
      : lc_(lc), ec_(ec), order_(order), sha_(lc), r_(lc) {}

  void assert_credential(EltW pkX, EltW pkY, EltW hash_tr,
                         OpenedAttribute oa[/* NUM_ATTR */],
                         const v8 now[/*kDateLen*/], const Witness& vw) const {
    Ecdsa ecc(lc_, ec_, order_);

    ecc.verify_signature3(pkX, pkY, vw.e_, vw.sig_);
    ecc.verify_signature3(vw.dpkx_, vw.dpky_, hash_tr, vw.dpk_sig_);

    sha_.assert_message(kMaxSHABlocks, vw.nb_, vw.in_, vw.sig_sha_);

    const Memcmp<LogicCircuit> CMP(lc_);
    // // validFrom <= now
    lc_.assert1(CMP.leq(kDateLen, &vw.in_[84], &now[0]));

    // // now <= validUntil
    lc_.assert1(CMP.leq(kDateLen, &now[0], &vw.in_[92]));

    // // DPK_{x,y}
    EltW dpkx = repack(vw.in_, 100);
    EltW dpky = repack(vw.in_, 132);
    lc_.assert_eq(&dpkx, vw.dpkx_);
    lc_.assert_eq(&dpky, vw.dpky_);

    // Attributes parsing
    const v8 zz = lc_.template vbit<8>(0xff);  // cannot appear in strings
    std::vector<v8> cmp_buf(32);
    for (size_t ai = 0; ai < kNumAttr; ++ai) {
      r_.shift(oa[ai].ind, 32, &cmp_buf[0], kMaxMsoLen, vw.in_, zz, 3);
      assert_attribute(32, oa[ai].len, &cmp_buf[0], &oa[ai].v1[0]);
    }
  }

 private:
  // Checks that an attribute id or attribute value is as expected.
  // The len parameter holds the byte length of the expected id or value.
  void assert_attribute(size_t max, const v8& vlen, const v8 got[/*max*/],
                        const v8 want[/*max*/]) const {
    for (size_t j = 0; j < max; ++j) {
      auto ll = lc_.vlt(j, vlen);
      auto cmp = lc_.veq(got[j], want[j]);
      lc_.assert_implies(&ll, cmp);
    }
  }

  Flatsha sha_;
  RoutingL r_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_H_

#include <cstddef>
#include <cstdint>
#include <vector>

#include "circuits/base64/decode.h"
#include "circuits/compiler/compiler.h"
#include "circuits/ecdsa/verify_circuit.h"
#include "circuits/jwt/jwt_constants.h"
#include "circuits/logic/bit_plucker.h"
#include "circuits/logic/routing.h"
#include "circuits/sha/flatsha256_circuit.h"

namespace proofs {

template <class LogicCircuit, class Field, class EC>
class JWT {
  using EltW = typename LogicCircuit::EltW;
  using BitW = typename LogicCircuit::BitW;
  using Nat = typename Field::N;
  using Ecdsa = VerifyCircuit<LogicCircuit, Field, EC>;
  using EcdsaWitness = typename Ecdsa::Witness;
  using v8 = typename LogicCircuit::v8;
  using v256 = typename LogicCircuit::v256;
  using Flatsha =
      FlatSHA256Circuit<LogicCircuit,
                        BitPlucker<LogicCircuit, kSHAJWTPluckerBits>>;
  using ShaBlockWitness = typename Flatsha::BlockWitness;
  using sha_packed_v32 = typename Flatsha::packed_v32;
  using vind = typename LogicCircuit::template bitvec<kJWTIndexBits>;

 public:
  struct OpenedAttribute {
    v8 attr[32]; /* 32b representing attribute name in be. */
    v8 v1[64];   /* 64b of attribute value */
  };

  class Witness {
   public:
    EltW r_, s_, e_;
    EcdsaWitness jwt_sig_;
    v8 preimage_[64 * kMaxJWTSHABlocks];
    v256 e_bits_;
    ShaBlockWitness sha_[kMaxJWTSHABlocks];
    v8 nb_; /* index of sha block that contains the real hash  */
    std::vector<vind> attr_ind_;
    std::vector<v8> attr_id_len_;
    std::vector<v8> attr_value_len_;
    vind payload_ind_, payload_len_;

    void input(QuadCircuit<Field>& Q, const LogicCircuit& lc, size_t na) {
      r_ = Q.input();
      s_ = Q.input();
      e_ = Q.input();
      jwt_sig_.input(Q);
      for (size_t i = 0; i < 64 * kMaxJWTSHABlocks; ++i) {
        preimage_[i] = lc.template vinput<8>();
      }
      e_bits_ = lc.template vinput<256>();
      for (size_t j = 0; j < kMaxJWTSHABlocks; ++j) {
        sha_[j].input(Q);
      }
      nb_ = lc.template vinput<8>();

      for (size_t j = 0; j < na; ++j) {
        attr_ind_.push_back(lc.template vinput<kJWTIndexBits>());
        attr_id_len_.push_back(lc.template vinput<8>());
        attr_value_len_.push_back(lc.template vinput<8>());
      }
      payload_ind_ = lc.template vinput<kJWTIndexBits>();
      payload_len_ = lc.template vinput<kJWTIndexBits>();
    }
  };

  explicit JWT(const LogicCircuit& lc, const EC& ec, const Nat& order)
      : lc_(lc), ec_(ec), order_(order), sha_(lc), r_(lc) {}

  //  The assert_jwt_attributes circuit verifies the following claims:
  //    1. There exists a hash digest e and a signature (r,s) on e
  //       under the public key (pkX, pkY).
  //    2. There exists a msg, and the hash of msg is equal to e.
  //    3. The JWT message is decoded correctly from base64.
  //    4. The decoded message is equal to the payload.header.
  //    5. The header contains alg:ESP256. [TODO]
  //    6. The attributes occur as <ID>":"<VALUE>" in the payload.body.
  //
  // Note that the soundness of (6) relies on assumptions about the format of
  // the JWT. The issuer cannot add spaces, cannot escape quotes in the body,
  // and the character : should only appear as a separator.
  void assert_jwt_attributes(EltW pkX, EltW pkY,
                             OpenedAttribute oa[/* NUM_ATTR */],
                             Witness& vw) const {
    Ecdsa ecc(lc_, ec_, order_);

    ecc.verify_signature3(pkX, pkY, vw.e_, vw.jwt_sig_);

    sha_.assert_message_hash(kMaxJWTSHABlocks, vw.nb_, vw.preimage_, vw.e_bits_,
                             vw.sha_);
    lc_.vassert_is_bit(vw.e_bits_);

    // Check that the e_bits_ match the EltW for e used in the signature.
    auto twok = lc_.one();
    auto est = lc_.konst(0);
    for (size_t i = 0; i < 256; ++i) {
      est = lc_.axpy(&est, twok, lc_.eval(vw.e_bits_[i]));
      lc_.f_.add(twok, twok);
    }
    lc_.assert_eq(&est, vw.e_);

    // Assert the attribute equality
    const v8 zz = lc_.template vbit<8>(0);  // cannot appear in strings
    std::vector<v8> shift_buf(64 * kMaxJWTSHABlocks);

    // First shift the payload into the shift_buf.
    r_.shift(vw.payload_ind_, 64 * (kMaxJWTSHABlocks - 2), shift_buf.data(),
             64 * kMaxJWTSHABlocks, vw.preimage_, zz, 3);

    // Decode the entire payload. A possible improvement is to decode just
    // the portion necessary.
    std::vector<v8> dec_buf(64 * kMaxJWTSHABlocks);
    Base64Decoder<LogicCircuit> b64(lc_);
    b64.base64_rawurl_decode_len(shift_buf.data(), dec_buf.data(),
                                 64 * (kMaxJWTSHABlocks - 2), vw.payload_len_);

    // For each attribute, shift the decoded payload so that the
    // attribute is at the beginning of B. Verify the attribute id, the
    // json separator, the attribute value, and the end quote.
    for (size_t i = 0; i < vw.attr_ind_.size(); ++i) {
      v8 B[32 + 3 + 64 + 1];

      // Check that values of the attribute_id.
      r_.shift(vw.attr_ind_[i], 100, B, dec_buf.size(), dec_buf.data(), zz, 3);
      assert_string_eq(32, vw.attr_id_len_[i], B, oa[i].attr);

      r_.shift(vw.attr_id_len_[i], 100, B, 100, B, zz, 3);
      uint8_t sep[3] = {'"', ':', '"'};
      for (size_t j = 0; j < 3; ++j) {
        auto want_j = lc_.template vbit<8>(sep[j]);
        lc_.vassert_eq(&B[j], want_j);
      }

      auto three = lc_.template vbit<2>(3);
      r_.shift(three, 100, B, 100, B, zz, 3);

      assert_string_eq(64, vw.attr_value_len_[i], B, oa[i].v1);

      r_.shift(vw.attr_value_len_[i], 100, B, 100, B, zz, 3);

      auto end_quote = lc_.template vbit<8>('"');
      lc_.vassert_eq(&B[0], end_quote);
    }
  }

  void assert_string_eq(size_t max, const v8& len, const v8 got[/*max*/],
                        const v8 want[/*max*/]) const {
    for (size_t j = 0; j < max; ++j) {
      auto ll = lc_.vlt(j, len);
      auto same = lc_.eq(8, got[j].data(), want[j].data());
      lc_.assert_implies(&ll, same);
    }
  }

 private:
  const LogicCircuit& lc_;
  const EC& ec_;
  const Nat& order_;
  Flatsha sha_;
  Routing<LogicCircuit> r_;
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_REVOCATION_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_REVOCATION_H_

#include <cstddef>

#include "circuits/compiler/compiler.h"
#include "circuits/ecdsa/verify_circuit.h"
#include "circuits/logic/bit_plucker.h"
#include "circuits/mdoc/mdoc_revocation_constants.h"
#include "circuits/sha/flatsha256_circuit.h"

namespace proofs {

// The first revocation approach works for small lists that are expected to
// be small. In this case, the prover simply asserts that their identifier is
// different from all the identifiers in the list.
template <class LogicCircuit>
class MdocRevocationList {
  using EltW = typename LogicCircuit::EltW;

 public:
  explicit MdocRevocationList(const LogicCircuit& lc) : lc_(lc) {}

  // This function asserts that a given identifier is not on a revocation list.
  // The method is to assert that Prod_i (list[i) - id) != 0.
  void assert_not_on_list(EltW list[], size_t list_size,
                          /* the witness */ EltW id, EltW prodinv) const {
    EltW prod =
        lc_.mul(0, list_size, [&](size_t i) { return lc_.sub(&list[i], id); });
    EltW want_one = lc_.mul(&prod, prodinv);
    lc_.assert_eq(&want_one, lc_.konst(lc_.one()));
  }

  const LogicCircuit& lc_;
};

// The second revocation approachs works for larger lists. In this case, the
// prover retrieves a witness that their credential is *not* on the revoked
// list by presenting a signature of the span (l,r) and proving that their
// revocation identifier rev_id satisfied l < rev_id < r.
// Specifically, the format of the span is:
//   epoch || l || r
// where epoch is a 64 bit integer, l and r are 256 bit integers. All of
// the values are encoded in little endian order.
template <class LogicCircuit, class Field, class EC>
class MdocRevocationSpan {
  using EltW = typename LogicCircuit::EltW;
  using Nat = typename Field::N;
  using Ecdsa = VerifyCircuit<LogicCircuit, Field, EC>;
  using EcdsaWitness = typename Ecdsa::Witness;
  using v8 = typename LogicCircuit::v8;
  using v256 = typename LogicCircuit::v256;
  using Flatsha =
      FlatSHA256Circuit<LogicCircuit,
                        BitPlucker<LogicCircuit, kSHARevocationPluckerBits>>;
  using ShaBlockWitness = typename Flatsha::BlockWitness;
  using sha_packed_v32 = typename Flatsha::packed_v32;

 public:
  class Witness {
   public:
    EltW r_, s_, e_;
    EcdsaWitness rev_sig_;
    v8 preimage_[64 * 2];  //  epoch || l || r  in little endian order
    v256 id_bits_;
    v256 e_bits_;
    ShaBlockWitness sha_[2];

    void input(QuadCircuit<Field>& Q, const LogicCircuit& lc) {
      r_ = Q.input();
      s_ = Q.input();
      e_ = Q.input();
      rev_sig_.input(Q);
      for (size_t i = 0; i < 64 * 2; ++i) {
        preimage_[i] = lc.template vinput<8>();
      }
      id_bits_ = lc.template vinput<256>();
      e_bits_ = lc.template vinput<256>();
      for (size_t j = 0; j < 2; j++) {
        sha_[j].input(Q);
      }
    }
  };

  explicit MdocRevocationSpan(const LogicCircuit& lc, const EC& ec,
                              const Nat& order)
      : lc_(lc), ec_(ec), order_(order), sha_(lc) {}

  // This function asserts that id is not on the revocation list by verifying
  // that the signature (r,s) on the span (l,r) is valid, and then verifying
  // that l < id < r.  The argument (craPkX, craPkY) represent the public key
  // of the issuer of the revocation list.
  void assert_not_on_list(EltW craPkx, EltW craPkY,
                          /* the witness */ EltW id, Witness& vw) const {
    Ecdsa ecc(lc_, ec_, order_);

    ecc.verify_signature3(craPkx, craPkY, vw.e_, vw.rev_sig_);

    lc_.vassert_is_bit(vw.e_bits_);
    lc_.vassert_is_bit(vw.id_bits_);

    // Check that e = hash(epoch || l || r)
    auto two = lc_.template vbit<8>(2);
    sha_.assert_message_hash(2, two, vw.preimage_, vw.e_bits_, vw.sha_);

    // Check that the bits of e match the EltW for e.
    auto twok = lc_.one();
    auto est = lc_.konst(0);
    for (size_t i = 0; i < 256; ++i) {
      est = lc_.axpy(&est, twok, lc_.eval(vw.e_bits_[i]));
      lc_.f_.add(twok, twok);
    }
    lc_.assert_eq(&est, vw.e_);

    // // Check that l < id < r
    v256 ll, rr;
    for (size_t i = 0; i < 256; ++i) {
      ll[i] = vw.preimage_[8 + i / 8][i % 8];
      rr[i] = vw.preimage_[40 + i / 8][i % 8];
    }
    lc_.assert1(lc_.vlt(&ll, vw.id_bits_));
    lc_.assert1(lc_.vlt(&vw.id_bits_, rr));
  }

  const LogicCircuit& lc_;
  const EC& ec_;
  const Nat& order_;
  Flatsha sha_;
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_REVOCATION_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_SIGNATURE_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_SIGNATURE_H_

#include <cstddef>

#include "circuits/compiler/compiler.h"
#include "circuits/ecdsa/verify_circuit.h"
#include "circuits/logic/bit_plucker.h"
#include "circuits/mac/mac_circuit.h"

namespace proofs {

// This class creates a circuit to verify the signatures in an MDOC.
// There are 2 signatures:
//    1. A signature on the MSO by the issuer of the MDOC: The public
//       key of the issuer is given as input for now. Later, it can be
//       one among a list of issuers.  While the signer is public, the
//       message is private, and thus its hash is committed in the witness.
//    2. A signature on the transcript provided during a "Show" operation:
//       the signature is under a device public key that is specified in the
//       MSO.  Thus, the signing key is private (and committed), but the
//       message is public.
template <class LogicCircuit, class Field, class EC>
class MdocSignature {
  using EltW = typename LogicCircuit::EltW;
  using Elt = typename LogicCircuit::Elt;
  using Nat = typename Field::N;
  using v128 = typename LogicCircuit::v128;
  using v256 = typename LogicCircuit::v256;
  using Ecdsa = VerifyCircuit<LogicCircuit, Field, EC>;
  using EcdsaWitness = typename Ecdsa::Witness;
  using MacBitPlucker = BitPlucker<LogicCircuit, kMACPluckerBits>;
  using packed_v256 = typename MacBitPlucker::packed_v256;
  using mac = MAC<LogicCircuit, MacBitPlucker>;
  using MACWitness = typename mac::Witness;

  const LogicCircuit& lc_;
  const EC& ec_;
  const Nat& order_;

 public:
  class Witness {
   public:
    EltW e_;
    EltW dpkx_, dpky_;

    EcdsaWitness mdoc_sig_;
    EcdsaWitness dpk_sig_;
    MACWitness macs_[3];

    void input(QuadCircuit<Field>& Q, const LogicCircuit& lc) {
      e_ = Q.input();
      dpkx_ = Q.input();
      dpky_ = Q.input();

      mdoc_sig_.input(Q);
      dpk_sig_.input(Q);
      for (size_t i = 0; i < 3; ++i) {
        macs_[i].input(lc, Q);
      }
    }
  };

  explicit MdocSignature(const LogicCircuit& lc, const EC& ec, const Nat& order)
      : lc_(lc), ec_(ec), order_(order) {}

  // This function is used to verify the signatures in an MDOC.
  // The circuit verifies the following claims:
  //    1. There exists a hash digest e and a signature (r,s) on e
  //       under the public key (pkX, pkY).
  //    2. The MAC of e under the secret mac key (a_v+a_pe) is mac_e.
  //    3. There exists a device public key (dpkX, dpky) and a signature (r,s)
  //       on the value hash_tr.
  //    4. The MAC of the device public key (dpkX, dpky) under the secret MAC
  //       key (a_v + apdk) is mac_dkpX and mac_dpkY respectively.
  void assert_signatures(EltW pkX, EltW pkY, EltW hash_tr, v128 mac_e[2],
                         v128 mac_dpkX[2], v128 mac_dpkY[2], v128 a_v,
                         Witness& vw) const {
    Ecdsa ecc(lc_, ec_, order_);
    mac macc(lc_);

    ecc.verify_signature3(pkX, pkY, vw.e_, vw.mdoc_sig_);
    ecc.verify_signature3(vw.dpkx_, vw.dpky_, hash_tr, vw.dpk_sig_);

    macc.verify_mac(vw.e_, mac_e, a_v, vw.macs_[0], order_);
    macc.verify_mac(vw.dpkx_, mac_dpkX, a_v, vw.macs_[1], order_);
    macc.verify_mac(vw.dpky_, mac_dpkY, a_v, vw.macs_[2], order_);
  }

  // This function is similar to assert_signatures, but it also hides the
  // public key of the issuer. Instead, it verifies that the issuer's public
  // key belongs in a list of 50 public keys that are supplied as input. The
  // issuer pk lists are assumed to be trusted inputs, i.e., it is the
  // caller's responsibility to ensure that (issuer_pkX[i], issuer_pkY[i]) is
  // a valid curve point for i=0..49.  The caller is also responsible for
  // ensuring that issuer_pkY[i] != -issuer_pkY[j] for i != j.
  // However, it is OK for the caller to repeat the same key in the list.
  void assert_signatures_with_issuer_list(
      EltW hash_tr, v128 mac_e[2], v128 mac_dpkX[2], v128 mac_dpkY[2], v128 a_v,
      EltW issuer_pkX[/*max_issuers*/], EltW issuer_pkY[/*max_issuers*/],
      size_t max_issuers,
      // private inputs begin here
      EltW pkX, EltW pkY, Witness& vw) const {
    assert_signatures(pkX, pkY, hash_tr, mac_e, mac_dpkX, mac_dpkY, a_v, vw);

    // Verify that the issuer's public key is one of the 50 keys in the list.
    // This is done by computing the difference between pkX and issuer_pkX[i]
    // for i=0..49, and asserting that the product of the differences is zero.
    //
    // We argue that it suffices to verify that pkX is on the list and pkY is
    // on the list independently.  Suppose a malicious prover sets pkX to be
    // equal to the j-th key in issuer_pkX and sets pkY to be the k-th key in
    // issuer_pkY, where j != k.   If (pkX, pkY) is not a curve point, then the
    // assert_signatures() routine will fail.  However, for each X on the curve,
    // there are only 2 possible Y values, namely, +-Y. By the constraints
    // imposed on issuer_pkY, we know that issuer_pkY[j] is on the curve, and
    // that -issuer_pkY[j] does not occur in the issuer_pkY list.  Thus, it is
    // not possible for a witness to pass all checks and for k != j.
    EltW goodXKey = lc_.mul(0, max_issuers, [&](size_t i) {
      return lc_.sub(&issuer_pkX[i], pkX);
    });
    lc_.assert0(goodXKey);

    EltW goodYKey = lc_.mul(0, max_issuers, [&](size_t i) {
      return lc_.sub(&issuer_pkY[i], pkY);
    });
    lc_.assert0(goodYKey);
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_SIGNATURE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_H_

#include <stddef.h>
#include <stdint.h>

#include <array>
#include <vector>

#include "circuits/cbor_parser/cbor_constants.h"
#include "circuits/cbor_parser/cbor_pluck.h"
#include "circuits/cbor_parser/scan.h"
#include "circuits/logic/bit_adder.h"
#include "circuits/logic/memcmp.h"
#include "circuits/logic/routing.h"
#include "util/panic.h"

namespace proofs {
template <class Logic, size_t IndexBits = CborConstants::kIndexBits>
class Cbor {
 public:
  using Field = typename Logic::Field;
  using EltW = typename Logic::EltW;
  using BitW = typename Logic::BitW;
  using v8 = typename Logic::v8;
  static constexpr size_t kIndexBits = IndexBits;
  static constexpr size_t kNCounters = CborConstants::kNCounters;
  using bv_counters = typename Logic::template bitvec<kNCounters>;

  // a bitvector that contains an index into the input
  // (byte) array.
  using vindex = typename Logic::template bitvec<kIndexBits>;

  // does not yet work in binary fields
  static_assert(!Field::kCharacteristicTwo);

  explicit Cbor(const Logic& l)
      : l_(l), ba_count_(l), ba_byte_(l), ba_index_(l), bp_(l) {}

  struct global_witness {
    EltW invprod_decode;  // inverse of a certain product, see assert_decode()
    EltW cc0;             // initial value of counter[0]
    EltW invprod_parse;   // inverse of a certain product, see assert_parse()
  };

  struct position_witness {
    EltW encoded_sel_header;
  };

  //------------------------------------------------------------
  // Decoder (lexer)
  //------------------------------------------------------------
  struct decode {
    BitW arrayp;
    BitW mapp;
    BitW itemsp;
    BitW stringp;
    BitW tagp;
    BitW specialp;
    BitW simple_specialp;  // One of false, true, null, or undefined.
    BitW count0_23;
    BitW count24;
    BitW invalid;
    BitW length_plus_next_v8;
    BitW count_is_next_v8;
    BitW header;
    EltW length;  // of this item
    EltW as_field_element;
    EltW count_as_field_element;
    v8 as_bits;
  };

  // extract whatever we can from one v8
  struct decode decode_one_v8(const v8& v) const {
    const Logic& L = l_;  // shorthand
    struct decode s;
    L.vassert_is_bit(v);

    // v = type:3 count:5
    auto count = L.template slice<0, 5>(v);
    auto type = L.template slice<5, 8>(v);

    s.itemsp = L.veqmask(type, /*mask*/ 0b110, /*val*/ 0b100);
    s.stringp = L.veqmask(type, /*mask*/ 0b110, /*val*/ 0b010);

    s.specialp = L.veq(type, 7);
    s.tagp = L.veq(type, 6);
    s.arrayp = L.land(&s.itemsp, L.lnot(type[0]));
    s.mapp = L.land(&s.itemsp, type[0]);

    // count0_23 = (0 <= count < 24) = ~(count == 11xxx)
    s.count0_23 = L.lnot(L.veqmask(count, /*mask*/ 0b11000, /*val*/ 0b11000));

    s.count24 = L.veq(count, 24);

    BitW count20_23 = L.veqmask(count, /*mask*/ 0b11100, /*val*/ 0b10100);
    s.simple_specialp = L.land(&s.specialp, count20_23);

    // stringp && count24
    s.length_plus_next_v8 =
        L.veqmask(v, /*mask*/ 0b110'11111, /*val*/ 0b010'11000);

    // itemsp && count24
    s.count_is_next_v8 =
        L.veqmask(v, /*mask*/ 0b110'11111, /*val*/ 0b100'11000);

    // invalid = (specialp && !simple_specialp) || (!count24 && !count0_23)
    auto lhs = L.land(&s.specialp, L.lnot(s.simple_specialp));
    auto bad_count = L.lor_exclusive(&s.count24, s.count0_23);
    s.invalid = L.lor(&lhs, L.lnot(bad_count));

    s.count_as_field_element = ba_count_.as_field_element(count);

    // Length is the length of the item, including the header.
    //
    //    1          for header
    //   +1          if (count24)
    //   +count      if (stringp && count0_23);
    s.length = L.konst(1);
    s.length = L.add(&s.length, L.eval(s.count24));
    auto str_23 = L.land(&s.stringp, s.count0_23);
    auto e_str_23 = L.eval(str_23);
    auto adjust_if_string = L.mul(&e_str_23, s.count_as_field_element);
    s.length = L.add(&s.length, adjust_if_string);

    s.as_field_element = ba_byte_.as_field_element(v);
    s.as_bits = v;

    s.header = L.bit(0);  // for now
    return s;
  }

  void assert_decode(size_t n, const decode ds[/*n*/],
                     const position_witness pw[/*n*/],
                     const global_witness& gw) const {
    const Logic& L = l_;  // shorthand
    Scan<Logic> SC(l_);

    // -------------------------------------------------------------
    // Decoder didn't fail
    for (size_t i = 0; i < n; ++i) {
      L.assert_implies(&ds[i].header, L.lnot(ds[i].invalid));
    }
    // if LENGTH_PLUS_NEXT_V8 is TRUE in the last position,
    // then the input is invalid.
    L.assert_implies(&ds[n - 1].header, L.lnot(ds[n - 1].length_plus_next_v8));

    // if COUNT_IS_NEXT_V8 is TRUE in the last position,
    // then the input is invalid.
    L.assert_implies(&ds[n - 1].header, L.lnot(ds[n - 1].count_is_next_v8));

    // -------------------------------------------------------------
    // Headers are where they are supposed to be.
    // First, compute the segmented scan
    //   slen[i] = header[i] ? length[i] : (slen[i-1] + mone[i])
    std::vector<EltW> mone(n);
    std::vector<BitW> header(n);
    std::vector<EltW> length(n);
    std::vector<EltW> slen_next(n);

    for (size_t i = 0; i + 1 < n; ++i) {
      mone[i] = L.konst(L.mone());
      header[i] = ds[i].header;
      length[i] = ds[i].length;
      if (i + 1 < n) {
        auto len_i =
            L.lmul(&ds[i].length_plus_next_v8, ds[i + 1].as_field_element);
        length[i] = L.add(&length[i], len_i);
      }
    }

    SC.add(n, slen_next.data(), header.data(), length.data(), mone.data());

    // Now check the headers.
    {
      // "The first position is a header"
      L.assert1(header[0]);
    }

    {
      auto one = L.konst(1);
      // "\A I : (SLEN_NEXT[I] == 1)  IFF  HEADER[I+1]"
      {
        // "\A I : HEADER[I+1] => (SLEN_NEXT[I] == 1)"
        for (size_t i = 0; i + 1 < n; ++i) {
          auto implies = L.lmul(&header[i + 1], L.sub(&slen_next[i], one));
          L.assert0(implies);
        }
      }
      {
        // "\A I : (SLEN_NEXT[I] == 1) => HEADER[i+1] "
        // Verify via the invertibility of
        //
        //   PROD_{I, L} HEADER[I+1] ? 1 : (SLEN_NEXT[I] - 1)
        //
        auto f = [&](size_t i) {
          return L.mux(&header[i + 1], &one, L.sub(&slen_next[i], one));
        };
        EltW prod = L.mul(0, n - 1, f);
        auto want_one = L.mul(&prod, gw.invprod_decode);
        L.assert_eq(&want_one, one);
      }
    }
  }

  //------------------------------------------------------------
  // Parser
  //------------------------------------------------------------
  using counters = std::array<EltW, kNCounters>;
  struct parse_output {
    bv_counters sel;
    counters c;
  };

  void parse(size_t n, parse_output ps[/*n*/], const decode ds[/*n*/],
             const position_witness pw[/*n*/], const global_witness& gw) const {
    std::vector<EltW> ddss(n);
    std::vector<BitW> SS(n);
    std::vector<EltW> AA(n);
    std::vector<EltW> BB(n);

    const Logic& L = l_;  // shorthand
    Scan<Logic> SC(l_);

    for (size_t i = 0; i < n; ++i) {
      ps[i].sel = bp_.pluckj(pw[i].encoded_sel_header);
    }

    auto mone = L.konst(L.mone());
    for (size_t l = 0; l < kNCounters; ++l) {
      for (size_t i = 0; i < n; ++i) {
        // at the selected headers, decrement the level-L counter.
        auto dp = L.land(&ds[i].header, ps[i].sel[l]);
        ddss[i] = L.lmul(&dp, mone);
      }

      if (l == 0) {
        // do level-0 as an unsegmented parallel prefix
        // on DDSS starting at CC0.
        // One can achieve the same effect by using the segmented prefix
        // after initializing SS and AA as follows:
        //
        //   SS[0] = L.bit(1);
        //   AA[0] = gw.cc0;
        //   for (size_t i = 1; i < n; ++i) {
        //     SS[i] = L.bit(0);
        //     AA[i] = L.konst(0);
        //   }
        //
        // The compiler is smart enough to constant-fold the segment
        // SS[i] and produces the same circuit in both cases, but
        // there is no point in wasting compiler time and the
        // unsegmented prefix is more straightforward anyway.
        //
        // Note that AA, SS are uninitialized here.  They will be initialized
        // below for the next level.
        ddss[0] = gw.cc0;
        SC.add(n, BB.data(), ddss.data());
      } else {
        SC.add(n, BB.data(), SS.data(), AA.data(), ddss.data());
      }

      // output the result of the parallel prefix
      for (size_t i = 0; i < n; ++i) {
        ps[i].c[l] = BB[i];
      }

      // prepare SS, AA for the next level
      for (size_t i = 0; i < n; ++i) {
        EltW newc = L.eval(ds[i].tagp);
        EltW count = ds[i].count_as_field_element;
        if (i + 1 < n) {
          count = L.mux(&ds[i].count_is_next_v8, &ds[i + 1].as_field_element,
                        count);
        }
        newc = L.add(&newc, L.lmul(&ds[i].itemsp, count));
        newc = L.add(&newc, L.lmul(&ds[i].mapp, count));
        AA[i] = newc;

        auto sel = L.land(&ps[i].sel[l], ds[i].header);
        auto tag = L.lor(&ds[i].tagp, ds[i].itemsp);
        SS[i] = L.land(&sel, tag);
      }
    }

    // Assert that we don't want to start new segments at a level
    // that does not exist.
    for (size_t i = 0; i < n; ++i) {
      L.assert0(SS[i]);
    }
  }

  void assert_parse(size_t n, const decode ds[/*n*/],
                    const parse_output ps[/*n*/],
                    const global_witness& gw) const {
    const Logic& L = l_;  // shorthand

    for (size_t i = 0; i < n; ++i) {
      // "The SEL witnesses are mutually exclusive."
      // Verify by asserting that they are all bits
      // and that their sum (in the field) is a bit.
      auto sum = L.bit(0);
      for (size_t l = 0; l < kNCounters; ++l) {
        L.assert_is_bit(ps[i].sel[l]);
        sum = L.lor_exclusive(&sum, ps[i].sel[l]);
      }
      L.assert_is_bit(sum);

      // "at a header, at least one SEL bit is set"
      L.assert_implies(&ds[i].header, sum);
    }

    // "All counters are zero at the end of the input"
    // COUNTER[I][L] is the state of the parser at the end
    // of position I, so COUNTER[N-1][L] is the final state.
    for (size_t l = 0; l < kNCounters; ++l) {
      L.assert0(ps[n - 1].c[l]);
    }

    // SEL[0][0] is set.  We implicitly define COUNTER[-1][L] to make
    // this the correct choice.
    L.assert1(ps[0].sel[0]);

    for (size_t i = 0; i + 1 < n; ++i) {
      // "If SEL[I+1][L] is set, then COUNTER[I][L] is the nonzero
      // counter of maximal L.  (COUNTER[I][L] contains the output
      // counter of stage I, which affects SEL[I+1].)  Here we check
      // maximality:  COUNTER[I][J]=0 for J>L.  See below for
      // SEL[I+1][L] => (COUNTER[I][L] != 0).
      BitW b = ps[i + 1].sel[0];
      for (size_t l = 1; l < kNCounters; ++l) {
        // b => COUNTER[i][l] == 0
        L.assert0(L.lmul(&b, ps[i].c[l]));
        b = L.lor(&b, ps[i + 1].sel[l]);
      }
    }

    // "SEL[I+1][L] => (COUNTER[I][L] != 0)"
    // Check via the invertibility of
    //
    //    PROD_{I, L} SEL[I+1][L] ? COUNTER[I][L] : 1
    std::vector<EltW> prod(kNCounters);

    auto one = L.konst(1);
    for (size_t l = 0; l < kNCounters; ++l) {
      auto f = [&](size_t i) {
        return L.mux(&ps[i + 1].sel[l], &ps[i].c[l], one);
      };
      prod[l] = L.mul(0, n - 1, f);
    }

    EltW p = L.mul(0, kNCounters, [&](size_t l) { return prod[l]; });
    auto want_one = L.mul(&p, gw.invprod_parse);
    L.assert_eq(&want_one, one);
  }

  //------------------------------------------------------------
  // "J is the header of a string of length LEN containing BYTES"
  //------------------------------------------------------------
  void assert_text_at(size_t n, const vindex& j, size_t len,
                      const uint8_t bytes[/*len*/],
                      const decode ds[/*n*/]) const {
    const Logic& L = l_;  // shorthand
    const Routing<Logic> R(L);

    // we don't handle long strings
    proofs::check(len < 24, "len < 24");

    assert_header(n, j, ds);

    std::vector<EltW> A(n);
    for (size_t i = 0; i < n; ++i) {
      A[i] = ds[i].as_field_element;
    }

    // shift len+1 bytes, including the header.
    std::vector<EltW> B(len + 1);
    const EltW defaultA = L.konst(256);  // a constant that cannot appear in A[]
    R.shift(j, len + 1, B.data(), n, A.data(), defaultA, /*unroll=*/3);

    size_t expected_header = (3 << 5) + len;
    L.assert_eq(&B[0], L.konst(expected_header));
    for (size_t i = 0; i < len; ++i) {
      auto bi = L.konst(bytes[i]);
      L.assert_eq(&B[i + 1], bi);
    }
  }

  //------------------------------------------------------------
  // "J is a header containing unsigned U."
  //------------------------------------------------------------
  void assert_unsigned_at(size_t n, const vindex& j, uint64_t u,
                          const decode ds[/*n*/]) const {
    // only small u for now
    proofs::check(u < 24, "u < 24");

    size_t expected = (0 << 5) + u;
    assert_atom_at(n, j, l_.konst(expected), ds);
  }

  //------------------------------------------------------------
  // "J is a header containing negative U."  (U >= 0, and
  // CBOR distinguishes 0 from -0 apparently)
  //------------------------------------------------------------
  void assert_negative_at(size_t n, const vindex& j, uint64_t u,
                          const decode ds[/*n*/]) const {
    // only small u for now
    proofs::check(u < 24, "u < 24");

    size_t expected = (1 << 5) + u;
    assert_atom_at(n, j, l_.konst(expected), ds);
  }

  //------------------------------------------------------------
  // "J is a header containing a boolean primitive (0xF4 or 0xF5)."
  //
  //------------------------------------------------------------
  void assert_bool_at(size_t n, const vindex& j, bool val,
                      const decode ds[/*n*/]) const {
    size_t expected = (7 << 5) + (val ? 21 : 20);
    assert_atom_at(n, j, l_.konst(expected), ds);
  }

  // Helps assemble the checks for date assertions.
  void date_helper(size_t n, const vindex& j, const decode ds[/*n*/],
                   std::vector<v8>& B /* size 22 */) const {
    const Logic& L = l_;  // shorthand
    const Routing<Logic> R(L);
    assert_header(n, j, ds);

    std::vector<v8> A(n);
    for (size_t i = 0; i < n; ++i) {
      A[i] = ds[i].as_bits;
    }

    const v8 defaultA =
        L.template vbit<8>(0);  // a constant that cannot appear in A[]
    R.shift(j, 20 + 2, B.data(), n, A.data(), defaultA, /*unroll=*/3);

    // Check for tag: date/time string.
    L.vassert_eq(&B[0], L.template vbit<8>(0xc0));

    // Check for string(20)
    L.vassert_eq(&B[1], L.template vbit<8>(0x74));
  }

  //------------------------------------------------------------
  // "J is a header containing date d < now."  now is 20 bytes
  // in the format 2023-11-01T09:00:00Z
  //------------------------------------------------------------
  void assert_date_before_at(size_t n, const vindex& j, const v8 now[/* 20 */],
                             const decode ds[/*n*/]) const {
    const Logic& L = l_;  // shorthand
    const Memcmp<Logic> CMP(L);
    std::vector<v8> B(20 + 2);
    date_helper(n, j, ds, B);
    auto lt = CMP.lt(20, &B[2], now);
    L.assert1(lt);
  }

  //------------------------------------------------------------
  // "J is a header containing date d > now."  now is 20 bytes in the
  // format 2023-11-01T09:00:00Z
  // ------------------------------------------------------------
  void assert_date_after_at(size_t n, const vindex& j, const v8 now[/* 20 */],
                            const decode ds[/*n*/]) const {
    const Logic& L = l_;  // shorthand
    const Memcmp<Logic> CMP(L);
    std::vector<v8> B(20 + 2);
    date_helper(n, j, ds, B);
    auto lt = CMP.lt(20, now, &B[2]);
    L.assert1(lt);
  }

  //------------------------------------------------------------
  // "J is a header containing represented by the byte EXPECTED in the
  // input."
  //------------------------------------------------------------
  void assert_atom_at(size_t n, const vindex& j, const EltW& expected,
                      const decode ds[/*n*/]) const {
    const Logic& L = l_;  // shorthand
    const Routing<Logic> R(L);

    assert_header(n, j, ds);

    std::vector<EltW> A(n);
    for (size_t i = 0; i < n; ++i) {
      A[i] = ds[i].as_field_element;
    }

    EltW B[1];
    size_t unroll = 3;
    R.shift(j, 1, B, n, A.data(), L.konst(256), unroll);
    L.assert_eq(&B[0], expected);
  }

  //------------------------------------------------------------
  // "J is a header beginning a byte array of length LEN that
  // is the big-endian representation of EltW X."
  // ------------------------------------------------------------
  void assert_elt_as_be_bytes_at(size_t n, const vindex& j, size_t len, EltW X,
                                 const decode ds[/*n*/]) const {
    const Logic& L = l_;  // shorthand
    const Routing<Logic> R(L);

    std::vector<EltW> A(n);
    for (size_t i = 0; i < n; ++i) {
      A[i] = ds[i].as_field_element;
    }
    EltW tx = L.konst(0), k256 = L.konst(256);

    std::vector<EltW> B(2 + len);
    size_t unroll = 3;
    size_t si = 1;
    R.shift(j, len + 2, B.data(), n, A.data(), L.konst(0), unroll);
    if (len < 24) {
      size_t expected_header = (2 << 5) + len;
      auto eh = L.konst(expected_header);
      L.assert_eq(&B[0], eh);
    } else if (len < 256) {
      size_t expected_header = (2 << 5) + 24;
      auto eh = L.konst(expected_header);
      L.assert_eq(&B[0], eh);
      L.assert_eq(&B[1], L.konst(len));
      si = 2;
    } else {
      check(false, "len >= 256");
    }

    for (size_t i = 0; i < len; ++i) {
      auto tmp = L.mul(&tx, k256);
      tx = L.add(&tmp, B[i + si]);
    }

    L.assert_eq(&tx, X);
  }

  //------------------------------------------------------------
  // "Position j contains a header"
  //------------------------------------------------------------
  void assert_header(size_t n, const vindex& j, const decode ds[/*n*/]) const {
    const Logic& L = l_;  // shorthand

    L.vassert_is_bit(j);

    // giant dot product since the veq(j, .) terms are mutually exclusive.
    auto f = [&](size_t i) { return L.land(&ds[i].header, L.veq(j, i)); };
    L.assert1(L.lor_exclusive(0, n, f));
  }

  //------------------------------------------------------------
  // "A map starts at position j"
  //------------------------------------------------------------
  void assert_map_header(size_t n, const vindex& j,
                         const decode ds[/*n*/]) const {
    const Logic& L = l_;  // shorthand

    L.vassert_is_bit(j);

    // giant dot product since the veq(j, .) terms are mutually exclusive.
    auto f = [&](size_t i) {
      auto eq_ji = L.veq(j, i);
      auto dsi = L.land(&ds[i].mapp, ds[i].header);
      return L.land(&eq_ji, dsi);
    };
    L.assert1(L.lor_exclusive(0, n, f));
  }

  //------------------------------------------------------------
  // "Position M starts a map of level LEVEL.  (K, V) are headers
  // representing the J-th pair in that map"
  //------------------------------------------------------------
  void assert_map_entry(size_t n, const vindex& m, size_t level,
                        const vindex& k, const vindex& v, const vindex& j,
                        const decode ds[/*n*/],
                        const parse_output ps[/*n*/]) const {
    const Logic& L = l_;  // shorthand
    const Routing<Logic> R(L);

    assert_map_header(n, m, ds);
    assert_header(n, k, ds);
    assert_header(n, v, ds);

    for (size_t l = 0; l < kNCounters; ++l) {
      std::vector<EltW> A(n);
      for (size_t i = 0; i < n; ++i) {
        A[i] = ps[i].c[l];
      }

      // Select counters[m], counters[k], and counters[v].
      EltW cm, ck, cv;

      const size_t unroll = 3;
      R.shift(m, 1, &cm, n, A.data(), L.konst(0), unroll);
      R.shift(k, 1, &ck, n, A.data(), L.konst(0), unroll);
      R.shift(v, 1, &cv, n, A.data(), L.konst(0), unroll);

      if (l <= level) {
        // Counters[L] must agree at the key, value, and root
        // of the map.
        L.assert_eq(&cm, ck);
        L.assert_eq(&cm, cv);
      } else if (l == level + 1) {
        auto one = L.konst(1);
        auto two = L.konst(2);
        // LEVEL+1 counters must have the right number of decrements.
        // Specifically, if the counter at the map is N, then the j-th
        // key has N-(2*j+1) and the j-th value has N-(2*j+2)
        auto twoj = L.mul(&two, ba_index_.as_field_element(j));
        L.assert_eq(&cm, L.add(&ck, L.add(&twoj, one)));
        L.assert_eq(&cm, L.add(&cv, L.add(&twoj, two)));
      } else {
        // not sure if this is necessary, but all other counters
        // of CM are supposed to be zero.
        L.assert0(cm);
      }
    }
  }

  //------------------------------------------------------------
  // "JROOT is the first byte of the actual (unpadded) input and
  // all previous bytes are 0"
  //------------------------------------------------------------
  void assert_input_starts_at(size_t n, const vindex& jroot,
                              const vindex& input_len,
                              const decode ds[/*n*/]) const {
    const Logic& L = l_;  // shorthand

    L.assert1(L.vleq(input_len, n));
    L.assert1(L.vlt(jroot, n));
    auto tot = L.vadd(jroot, input_len);
    L.vassert_eq(tot, n);

    for (size_t i = 0; i < n; ++i) {
      L.assert0(L.lmul(&ds[i].as_field_element, L.vlt(i, jroot)));
    }
  }

  //------------------------------------------------------------
  // Utilities
  //------------------------------------------------------------
  // The circuit accepts up to N input positions, of which
  // INPUT_LEN are actual input and the rest are ignored.
  void decode_all(size_t n, decode ds[/*n*/], const v8 in[/*n*/],
                  const position_witness pw[/*n*/]) const {
    for (size_t i = 0; i < n; ++i) {
      ds[i] = decode_one_v8(in[i]);
      ds[i].header = bp_.pluckb(pw[i].encoded_sel_header);
    }
  }

  void decode_and_assert_decode(size_t n, decode ds[/*n*/], const v8 in[/*n*/],
                                const position_witness pw[/*n*/],
                                const global_witness& gw) const {
    decode_all(n, ds, in, pw);
    assert_decode(n, ds, pw, gw);
  }

  void decode_and_assert_decode_and_parse(size_t n, decode ds[/*n*/],
                                          parse_output ps[/*n*/],
                                          const v8 in[/*n*/],
                                          const position_witness pw[/*n*/],
                                          const global_witness& gw) const {
    decode_and_assert_decode(n, ds, in, pw, gw);
    parse(n, ps, ds, pw, gw);
    assert_parse(n, ds, ps, gw);
  }

 private:
  const Logic& l_;
  const BitAdder<Logic, 5> ba_count_;
  const BitAdder<Logic, 8> ba_byte_;
  const BitAdder<Logic, kIndexBits> ba_index_;
  const CborPlucker<Logic, kNCounters> bp_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ECDSA_VERIFY_CIRCUIT_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ECDSA_VERIFY_CIRCUIT_H_

#include <stddef.h>

#include "circuits/compiler/compiler.h"
#include "circuits/logic/bit_plucker.h"

namespace proofs {
// Verify ECDSA signature using triple scalar mult form.
//
// The field used by sumcheck is the base field of the elliptic curve.
// Compiled circuit: ecdsa verify
//  d: 7 wires: 21099 in: 1038 out:764 use:13911 ovh:7188 t:42963 cse:11351
//  notn:34724
//
template <class LogicCircuit, class Field, class EC>
class VerifyCircuit {
  using EltW = typename LogicCircuit::EltW;
  using BitW = typename LogicCircuit::BitW;
  using Elt = typename LogicCircuit::Elt;
  using Nat = typename Field::N;
  static constexpr size_t kBits = EC::kBits;
  using Bitvec = typename LogicCircuit::v256;

 public:
  struct Witness {
    EltW rx, ry;
    EltW pre[8];
    EltW rx_inv, s_inv, pk_inv;
    EltW bi[kBits];
    EltW int_x[kBits - 1];
    EltW int_y[kBits - 1];
    EltW int_z[kBits - 1];

    void input(QuadCircuit<Field>& Q) {
      rx = Q.input();
      ry = Q.input();
      rx_inv = Q.input();
      s_inv = Q.input();
      pk_inv = Q.input();
      for (size_t i = 0; i < 8; ++i) {
        pre[i] = Q.input();
      }
      for (size_t i = 0; i < kBits; ++i) {
        bi[i] = Q.input();
        if (i < kBits - 1) {
          int_x[i] = Q.input();
          int_y[i] = Q.input();
          int_z[i] = Q.input();
        }
      }
    }
  };

  VerifyCircuit(const LogicCircuit& lc, const EC& ec, const Nat& order)
      : lc_(lc), ec_(ec), k2_(lc_.elt(2)), k3_(lc_.elt(3)) {
    // Compute the bit representation of the order of the curve.
    for (size_t i = 0; i < ec.kBits; ++i) {
      bits_n_[i] = lc_.bit(order.bit(i));
    }
  }

  // This verify takes the triple (pkx,pky,e) and checks that there exists
  //     (r=rx, ry, s) such that:
  //           identity = g*e + pk*r + (rx,ry)*-s
  // It performs this check using a witness table that includes
  // (g+pk, g+r, r+pk, g+r+pk), a correction element,
  // bits of exponents (e, r, -s) s.t. each triple of bits is packed into {0,7},
  // and intermediate ec points in (x,y,z) form.  The bits are used to index the
  // witness table in order to compute the right-hand side in a loop.  The loop
  // is sliced by providing the intermediate results in order reduce depth.
  //
  // An external constraint will need to ensure that e \neq 0 (e.g.,
  // either the verifier checks this as part of the public input, or
  // the hash that defines e is produced in the circuit).  In our mdoc case,
  // we use the later checks for both signatures.
  //
  // Other checks:
  //   r is interpreted as both in the base field and the scalar field.
  //   As a result, rx_inv is provided to ensure that r != 0.
  //   Similarly, s_inv is provided to ensure that s != 0.
  //
  //   (rx,ry) is verified to be on the curve.
  //
  //   (pkx, pky) \neq identity, because we set pk_z=1, we verify that
  //    pkx != 0, and we ensure that (pkx,pky) is on the curve.
  //
  void verify_signature3(EltW pk_x, EltW pk_y, EltW e, const Witness& w) const {
    EltW zero = lc_.konst(lc_.zero());
    EltW one = lc_.konst(lc_.one());
    EltW gx = lc_.konst(ec_.gx_), gy = lc_.konst(ec_.gy_);

    // indices for the pre[] table, don't change order
    enum PreIndex {
      GPK_X = 0,
      GPK_Y,
      GR_X,
      GR_Y,
      RPK_X,
      RPK_Y,
      GRPK_X,
      GRPK_Y
    };

    // These variables hold the (e,r) exponents which are computed from the
    // bits of advice (e,r,-s).  They are compared with their expected values.
    EltW est = zero, rst = zero, sst = zero;

    // initialize at the 0 point, but these indices are reset on each loop
    EltW ax = zero, ay = one, az = zero;

    // =========
    // Verify the values received in the table are correct.
    // By verifying these values in parallel with using them, we can reduce
    // the depth of the resulting circuit.
    EltW cg_pkx, cg_pky, cg_pkz;
    EltW cr_pkx, cr_pky, cr_pkz;
    EltW cr_gx, cr_gy, cr_gz;
    EltW cr_g_pkx, cr_g_pky, cr_g_pkz;
    addE(cg_pkx, cg_pky, cg_pkz, gx, gy, one, pk_x, pk_y, one);
    addE(cr_gx, cr_gy, cr_gz, w.rx, w.ry, one, gx, gy, one);
    addE(cr_pkx, cr_pky, cr_pkz, w.rx, w.ry, one, pk_x, pk_y, one);
    addE(cr_g_pkx, cr_g_pky, cr_g_pkz, gx, gy, one, w.pre[RPK_X], w.pre[RPK_Y],
         one);
    point_equality(cg_pkx, cg_pky, cg_pkz, w.pre[GPK_X], w.pre[GPK_Y]);
    point_equality(cr_gx, cr_gy, cr_gz, w.pre[GR_X], w.pre[GR_Y]);
    point_equality(cr_pkx, cr_pky, cr_pkz, w.pre[RPK_X], w.pre[RPK_Y]);
    point_equality(cr_g_pkx, cr_g_pky, cr_g_pkz, w.pre[GRPK_X], w.pre[GRPK_Y]);

    EltW arr_x[] = {zero, gx,          pk_x,         w.pre[GPK_X],
                    w.rx, w.pre[GR_X], w.pre[RPK_X], w.pre[GRPK_X]};
    EltW arr_y[] = {one,  gy,          pk_y,         w.pre[GPK_Y],
                    w.ry, w.pre[GR_Y], w.pre[RPK_Y], w.pre[GRPK_Y]};
    EltW arr_z[] = {zero, one, one, one, one, one, one, one};
    EltW arr_e[] = {zero, one, zero, one, zero, one, zero, one};
    EltW arr_r[] = {zero, zero, one, one, zero, zero, one, one};
    EltW arr_s[] = {zero, zero, zero, zero, one, one, one, one};
    EltW arr_v[] = {one, one, one, one, one, one, one, one};

    EltMuxer<LogicCircuit, 3> xx(lc_, arr_x);
    EltMuxer<LogicCircuit, 3> yy(lc_, arr_y);
    EltMuxer<LogicCircuit, 3> zz(lc_, arr_z);
    EltMuxer<LogicCircuit, 3> ee(lc_, arr_e);
    EltMuxer<LogicCircuit, 3> rr(lc_, arr_r);
    EltMuxer<LogicCircuit, 3> ss(lc_, arr_s);
    EltMuxer<LogicCircuit, 3> vv(lc_, arr_v);

    Bitvec r_bits, s_bits;

    // Traverses the bits of the scalar from high-order to low-order.
    for (size_t i = 0; i < kBits; ++i) {
      // Use the arr{X..V} arrays and the muxer to pick the correct point
      // slice based on the bits of advice in the witness.
      EltW tx = xx.mux(w.bi[i]);
      EltW ty = yy.mux(w.bi[i]);
      EltW tz = zz.mux(w.bi[i]);

      // Update the exponent.
      EltW e_bi = ee.mux(w.bi[i]);
      EltW r_bi = rr.mux(w.bi[i]);
      EltW s_bi = ss.mux(w.bi[i]);
      auto k2 = lc_.konst(k2_);
      est = lc_.add(&e_bi, lc_.mul(&k2, est));
      rst = lc_.add(&r_bi, lc_.mul(&k2, rst));
      sst = lc_.add(&s_bi, lc_.mul(&k2, sst));
      r_bits[kBits - i - 1] = BitW(r_bi, ec_.f_);
      s_bits[kBits - i - 1] = BitW(s_bi, ec_.f_);

      // Verify that the advice bit is in [0,7].
      EltW range = vv.mux(w.bi[i]);
      lc_.assert_eq(&range, one);

      // Perform the basic add-dbl step in repeated squaring using the
      // muxed point {tx, ty, tz}.
      if (i > 0) {
        doubleE(ax, ay, az, ax, ay, az);
      }
      addE(ax, ay, az, ax, ay, az, tx, ty, tz);

      if (i < kBits - 1) {
        // Ensure that the resulting point is equal to the intermediate
        // point provided as input. Performing an explicit equality check
        // ensures that all intermediate witness points are on the curve.
        // This follows by induction. The first (ax,ay,az) is on the curve.
        // The addition formula ensures that the i-th (ax,ay,az) is on the
        // curve; equality ensures that the i-th witness is on the curve.
        lc_.assert_eq(&ax, w.int_x[i]);
        lc_.assert_eq(&ay, w.int_y[i]);
        lc_.assert_eq(&az, w.int_z[i]);

        // Use the intermediate (x,y,z) point as the next input.
        ax = w.int_x[i];
        ay = w.int_y[i];
        az = w.int_z[i];
      }
    }

    // Check that the aX,aZ points are 0.
    lc_.assert0(ax);
    lc_.assert0(az);

    // Check that the bits used for {e,rx} correspond to the input {e, rx}.
    lc_.assert_eq(&est, e);
    lc_.assert_eq(&rst, w.rx);

    // Check that (pk,py), (rx,ry) satisfy the curve equation.
    is_on_curve(pk_x, pk_y);
    is_on_curve(w.rx, w.ry);

    // Verify that exponents (r,s) are not zero.
    //   A witness is provided to ensure that both values have inverses in F.
    //   A bitwise comparison is done to ensure both are < |order| of EC.
    assert_nonzero(w.rx, w.rx_inv);
    assert_nonzero(sst, w.s_inv);
    assert_nonzero(pk_x, w.pk_inv);
    auto r_range = lc_.vlt(&r_bits, bits_n_);
    auto s_range = lc_.vlt(&s_bits, bits_n_);
    lc_.assert1(r_range);
    lc_.assert1(s_range);
  }

 private:
  void assert_nonzero(EltW x, EltW witness) const {
    auto maybe_one = lc_.mul(&x, witness);
    auto one = lc_.konst(lc_.one());
    lc_.assert_eq(&maybe_one, one);
  }

  void point_equality(EltW x, EltW y, EltW z, EltW p_x, EltW p_y) const {
    lc_.assert_eq(&x, lc_.mul(&z, p_x));
    lc_.assert_eq(&y, lc_.mul(&z, p_y));
  }

  void is_on_curve(EltW x, EltW y) const {
    // Check that y^2 = x^3 + ax + b
    auto yy = lc_.mul(&y, y);
    auto xx = lc_.mul(&x, x);
    auto xxx = lc_.mul(&x, xx);
    auto ax = lc_.mul(ec_.a_, x);
    auto b = lc_.konst(ec_.b_);
    auto axb = lc_.add(&ax, b);
    auto rhs = lc_.add(&axb, xxx);
    lc_.assert_eq(&yy, rhs);
  }

  void addE(EltW& X3, EltW& Y3, EltW& Z3, EltW X1, EltW Y1, EltW Z1, EltW X2,
            EltW Y2, EltW Z2) const {
    // The general case.
    // Algorithm 1: Complete, projective point addition for arbitrary prime
    // order short Weierstrass curves E/Fq : y^2 = x^3 + ax + b
    // The compiler seems to optimize the cases when Z1,Z2=1.
    EltW t0 = lc_.mul(&X1, X2);
    EltW t1 = lc_.mul(&Y1, Y2);
    EltW t2 = lc_.mul(&Z1, Z2);
    EltW t3 = lc_.add(&X1, Y1);
    EltW t4 = lc_.add(&X2, Y2);
    t3 = lc_.mul(&t3, t4);
    t4 = lc_.add(&t0, t1);
    t3 = lc_.sub(&t3, t4);
    t4 = lc_.add(&X1, Z1);
    EltW t5 = lc_.add(&X2, Z2);
    t4 = lc_.mul(&t4, t5);
    t5 = lc_.add(&t0, t2);
    t4 = lc_.sub(&t4, t5);
    t5 = lc_.add(&Y1, Z1);
    EltW X3t = lc_.add(&Y2, Z2);
    t5 = lc_.mul(&t5, X3t);
    X3t = lc_.add(&t1, t2);
    t5 = lc_.sub(&t5, X3t);
    auto a = lc_.konst(ec_.a_);
    EltW Z3t = lc_.mul(&a, t4);
    auto k3b = lc_.konst(ec_.k3b);
    X3t = lc_.mul(&k3b, t2);
    Z3t = lc_.add(&X3t, Z3t);
    X3t = lc_.sub(&t1, Z3t);
    Z3t = lc_.add(&t1, Z3t);
    EltW Y3t = lc_.mul(&X3t, Z3t);
    t1 = lc_.add(&t0, t0);
    t1 = lc_.add(&t1, t0);
    t2 = lc_.mul(&a, t2);
    t4 = lc_.mul(&k3b, t4);
    t1 = lc_.add(&t1, t2);
    t2 = lc_.sub(&t0, t2);
    t2 = lc_.mul(&a, t2);
    t4 = lc_.add(&t4, t2);
    t0 = lc_.mul(&t1, t4);
    Y3t = lc_.add(&Y3t, t0);
    t0 = lc_.mul(&t5, t4);
    X3t = lc_.mul(&t3, X3t);
    X3t = lc_.sub(&X3t, t0);
    t0 = lc_.mul(&t3, t1);
    Z3t = lc_.mul(&t5, Z3t);
    Z3t = lc_.add(&Z3t, t0);

    X3 = X3t;
    Y3 = Y3t;
    Z3 = Z3t;
  }

  void doubleE(EltW& X3, EltW& Y3, EltW& Z3, EltW X, EltW Y, EltW Z) const {
    // The general case.
    // Algorithm 3: Exception-free point doubling for arbitrary prime order
    // short Weierstrass curves E/Fq : y^2 = x^3 + ax + b.
    // The compiler will presumably optimize away 0 mults when a=0 and 1
    // mults when Z = 1.
    EltW t0 = lc_.mul(&X, X);
    EltW t1 = lc_.mul(&Y, Y);
    EltW t2 = lc_.mul(&Z, Z);
    EltW t3 = lc_.mul(&X, Y);
    t3 = lc_.add(&t3, t3);
    EltW Z3t = lc_.mul(&X, Z);
    Z3t = lc_.add(&Z3t, Z3t);
    auto a = lc_.konst(ec_.a_);
    auto k3b = lc_.konst(ec_.k3b);
    EltW X3t = lc_.mul(&a, Z3t);
    EltW Y3t = lc_.mul(&k3b, t2);
    Y3t = lc_.add(&X3t, Y3t);
    X3t = lc_.sub(&t1, Y3t);
    Y3t = lc_.add(&t1, Y3t);
    Y3t = lc_.mul(&X3t, Y3t);
    X3t = lc_.mul(&t3, X3t);
    Z3t = lc_.mul(&k3b, Z3t);
    t2 = lc_.mul(&a, t2);
    t3 = lc_.sub(&t0, t2);
    t3 = lc_.mul(&a, t3);
    t3 = lc_.add(&t3, Z3t);
    Z3t = lc_.add(&t0, t0);
    t0 = lc_.add(&Z3t, t0);
    t0 = lc_.add(&t0, t2);
    t0 = lc_.mul(&t0, t3);
    Y3t = lc_.add(&Y3t, t0);
    t2 = lc_.mul(&Y, Z);
    t2 = lc_.add(&t2, t2);
    t0 = lc_.mul(&t2, t3);
    X3t = lc_.sub(&X3t, t0);
    Z3t = lc_.mul(&t2, t1);
    Z3t = lc_.add(&Z3t, Z3t);
    Z3t = lc_.add(&Z3t, Z3t);

    X3 = X3t;
    Y3 = Y3t;
    Z3 = Z3t;
  }

  const LogicCircuit& lc_;
  const EC& ec_;

  Elt k2_, k3_;
  Bitvec bits_n_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ECDSA_VERIFY_CIRCUIT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_WITNESS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_WITNESS_H_

#include <stddef.h>
#include <string.h>

#include <cstdint>
#include <vector>

#include "algebra/static_string.h"
#include "arrays/dense.h"
#include "circuits/anoncred/small_io.h"
#include "circuits/ecdsa/verify_witness.h"
#include "circuits/logic/bit_plucker_encoder.h"
#include "circuits/mdoc/mdoc_witness.h"
#include "circuits/sha/flatsha256_witness.h"

namespace proofs {

class SmallOpenedAttribute {
 public:
  size_t ind_, len_;
  std::vector<uint8_t> value_;
  SmallOpenedAttribute(size_t ind, size_t len, const uint8_t* val, size_t vlen)
      : ind_(ind), len_(len), value_(val, val + vlen) {}
};

template <typename EC, typename Field, class ScalarField>
class SmallWitness {
  using ECField = typename EC::Field;
  using ECElt = typename ECField::Elt;
  using ECNat = typename ECField::N;
  using Elt = typename Field::Elt;
  using Nat = typename Field::N;
  using EcdsaWitness = VerifyWitness3<EC, ScalarField>;
  static constexpr size_t kMaxSHABlocks = 7;

 public:
  const EC ec_;
  Elt e_, e2_;      /* Issuer signature values. */
  Elt dpkx_, dpky_; /* device key */
  EcdsaWitness ew_, dkw_;
  uint8_t now_[kDateLen]; /* CBOR-formatted time used for expiry comparison. */

  FlatSHA256Witness::BlockWitness bw_[kMaxSHABlocks];
  uint8_t signed_bytes_[kMaxSHABlocks * 64];
  uint8_t numb_; /* Number of the correct sha block. */

  explicit SmallWitness(const EC& ec, const ScalarField& Fn)
      : ec_(ec), ew_(Fn, ec), dkw_(Fn, ec) {}

  void fill_sha(DenseFiller<Field>& filler,
                const FlatSHA256Witness::BlockWitness& bw) const {
    BitPluckerEncoder<Field, 3> BPENC(ec_.f_);
    for (size_t k = 0; k < 48; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.outw[k]));
    }
    for (size_t k = 0; k < 64; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.oute[k]));
      filler.push_back(BPENC.mkpacked_v32(bw.outa[k]));
    }
    for (size_t k = 0; k < 8; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.h1[k]));
    }
  }

  void fill_witness(DenseFiller<Field>& filler, bool small = false) const {
    filler.push_back(e_);
    filler.push_back(dpkx_);
    filler.push_back(dpky_);

    ew_.fill_witness(filler);
    dkw_.fill_witness(filler);

    filler.push_back(numb_, 8, ec_.f_);
    for (size_t i = 0; i < kMaxSHABlocks * 64; ++i) {
      filler.push_back(signed_bytes_[i], 8, ec_.f_);
    }
    for (size_t j = 0; j < kMaxSHABlocks; j++) {
      fill_sha(filler, bw_[j]);
    }
  }

  bool compute_witness(Elt pkX, Elt pkY, const uint8_t mdoc[/* len */],
                       size_t len, const uint8_t transcript[/* tlen */],
                       size_t tlen, const uint8_t tnow[/*kDateLen*/],
                       const StaticString& r, const StaticString& s,
                       const StaticString& dr, const StaticString& ds) {
    Nat ne = nat_from_hash<Nat>(mdoc, len);
    e_ = ec_.f_.to_montgomery(ne);

    // Parse (r,s).
    Nat nr = Nat(r);
    Nat ns = Nat(s);
    ew_.compute_witness(pkX, pkY, ne, nr, ns);

    Nat ne2 = nat_from_hash<Nat>(transcript, tlen);
    Nat nr2 = Nat(dr);
    Nat ns2 = Nat(ds);

    dpkx_ = ec_.f_.to_montgomery(nat_from_be<Nat>(&mdoc[100]));
    dpky_ = ec_.f_.to_montgomery(nat_from_be<Nat>(&mdoc[132]));
    e2_ = ec_.f_.to_montgomery(ne2);
    dkw_.compute_witness(dpkx_, dpky_, ne2, nr2, ns2);

    FlatSHA256Witness::transform_and_witness_message(len, mdoc, kMaxSHABlocks,
                                                     numb_, signed_bytes_, bw_);

    memcpy(now_, tnow, kDateLen);
    return true;
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_WITNESS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_FLATSHA256_CIRCUIT_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_FLATSHA256_CIRCUIT_H_

#include <stddef.h>

#include <cstdint>
#include <vector>

#include "circuits/compiler/compiler.h"
#include "circuits/logic/bit_adder.h"
#include "circuits/sha/sha256_constants.h"

namespace proofs {
// FlatSHA256Circuit
//
// Implements SHA256 hash function as an arithmetic circuit over the field F.
// The circuit is flattened, meaning that the SHA round function has been
// repeated in parallel instead of sequentially. As a result, the prover must
// provide the intermediate round values as witnesses.
//
// This package does not have any external dependencies on a SHA256 library.
//
// There are two versions of this function, one with standard bit inputs, and
// another with packed bit inputs. The later reduces the number of inputs at
// the cost of increasing the depth and number of wires. For example, the
// following shows the difference with pack parameter 2.
//
// FlatSHA256_Circuit.assert_transform_block
//  depth: 7 wires: 38029 in: 6657 out:128 use:30897 ovh:7132 t:166468 cse:9703
//  notn:113744
//
// FlatSHA256_Circuit.assert_transform_block_packed
//  depth: 9 wires: 65735 in: 3585 out:128 use:55486 ovh:10249 t:214653
//  cse:28135 notn:151504
//
//
template <class Logic, class BitPlucker>
class FlatSHA256Circuit {
 public:
  using v8 = typename Logic::v8;
  using v256 = typename Logic::v256;
  using v32 = typename Logic::v32;
  using EltW = typename Logic::EltW;
  using Field = typename Logic::Field;
  using packed_v32 = typename BitPlucker::packed_v32;

  const Logic& l_;
  BitPlucker bp_; /* public, so caller can encode input */

  struct BlockWitness {
    packed_v32 outw[48];
    packed_v32 oute[64];
    packed_v32 outa[64];
    packed_v32 h1[8];

    static packed_v32 packed_input(QuadCircuit<typename Logic::Field>& Q) {
      packed_v32 r;
      for (size_t i = 0; i < r.size(); ++i) {
        r[i] = Q.input();
      }
      return r;
    }

    void input(QuadCircuit<typename Logic::Field>& Q) {
      for (size_t k = 0; k < 48; ++k) {
        outw[k] = packed_input(Q);
      }
      for (size_t k = 0; k < 64; ++k) {
        oute[k] = packed_input(Q);
        outa[k] = packed_input(Q);
      }
      for (size_t k = 0; k < 8; ++k) {
        h1[k] = packed_input(Q);
      }
    }
  };

  explicit FlatSHA256Circuit(const Logic& l) : l_(l), bp_(l_) {}

  static packed_v32 packed_input(QuadCircuit<Field>& Q) {
    packed_v32 r;
    for (size_t i = 0; i < r.size(); ++i) {
      r[i] = Q.input();
    }
    return r;
  }

  void assert_transform_block(const v32 in[16], const v32 H0[8],
                              const v32 outw[48], const v32 oute[64],
                              const v32 outa[64], const v32 H1[8]) const {
    const Logic& L = l_;  // shorthand
    BitAdder<Logic, 32> BA(L);

    std::vector<v32> w(64);
    for (size_t i = 0; i < 16; ++i) {
      w[i] = in[i];
    }

    for (size_t i = 16; i < 64; ++i) {
      auto sw2 = sigma1(w[i - 2]);
      auto sw15 = sigma0(w[i - 15]);
      std::vector<v32> terms = {sw2, w[i - 7], sw15, w[i - 16]};
      w[i] = outw[i - 16];
      BA.assert_eqmod(w[i], BA.add(terms), 4);
    }

    v32 a = H0[0];
    v32 b = H0[1];
    v32 c = H0[2];
    v32 d = H0[3];
    v32 e = H0[4];
    v32 f = H0[5];
    v32 g = H0[6];
    v32 h = H0[7];

    for (size_t t = 0; t < 64; ++t) {
      auto s1e = Sigma1(e);
      auto ch = L.vCh(&e, &f, g);
      auto rt = L.vbit32(kSha256Round[t]);
      std::vector<v32> t1_terms = {h, s1e, ch, rt, w[t]};
      EltW t1 = BA.add(t1_terms);
      EltW sigma0 = BA.as_field_element(Sigma0(a));
      EltW vmaj = BA.as_field_element(L.vMaj(&a, &b, c));
      EltW t2 = BA.add(&sigma0, vmaj);

      h = g;
      g = f;
      f = e;
      e = oute[t];
      EltW ed = BA.as_field_element(d);
      BA.assert_eqmod(e, BA.add(&t1, ed), 6);
      d = c;
      c = b;
      b = a;
      a = outa[t];
      BA.assert_eqmod(a, BA.add(&t1, t2), 7);
    }

    BA.assert_eqmod(H1[0], BA.add(H0[0], a), 2);
    BA.assert_eqmod(H1[1], BA.add(H0[1], b), 2);
    BA.assert_eqmod(H1[2], BA.add(H0[2], c), 2);
    BA.assert_eqmod(H1[3], BA.add(H0[3], d), 2);
    BA.assert_eqmod(H1[4], BA.add(H0[4], e), 2);
    BA.assert_eqmod(H1[5], BA.add(H0[5], f), 2);
    BA.assert_eqmod(H1[6], BA.add(H0[6], g), 2);
    BA.assert_eqmod(H1[7], BA.add(H0[7], h), 2);
  }

  // Packed API.
  // H0 not packed, all others packed
  void assert_transform_block(const v32 in[16], const v32 H0[8],
                              const packed_v32 poutw[48],
                              const packed_v32 poute[64],
                              const packed_v32 pouta[64],
                              const packed_v32 pH1[8]) const {
    std::vector<v32> H1(8);
    std::vector<v32> outw(48);
    std::vector<v32> oute(64), outa(64);
    for (size_t i = 0; i < 8; ++i) {
      H1[i] = bp_.unpack_v32(pH1[i]);
    }
    for (size_t i = 0; i < 48; ++i) {
      outw[i] = bp_.unpack_v32(poutw[i]);
    }
    for (size_t i = 0; i < 64; ++i) {
      oute[i] = bp_.unpack_v32(poute[i]);
      outa[i] = bp_.unpack_v32(pouta[i]);
    }
    assert_transform_block(in, H0, outw.data(), oute.data(), outa.data(),
                           H1.data());
  }

  // all packed
  void assert_transform_block(const v32 in[16], const packed_v32 pH0[8],
                              const packed_v32 poutw[48],
                              const packed_v32 poute[64],
                              const packed_v32 pouta[64],
                              const packed_v32 pH1[8]) const {
    std::vector<v32> H0(8);
    for (size_t i = 0; i < 8; ++i) {
      H0[i] = bp_.unpack_v32(pH0[i]);
    }
    assert_transform_block(in, H0.data(), poutw, poute, pouta, pH1);
  }

  /* This method checks that the block witness corresponds to the iterated
     computation of the sha block transform on the input.
  */
  void assert_message(size_t max, const v8& nb, const v8 in[/* 64*max */],
                      const BlockWitness bw[/*max*/]) const {
    const Logic& L = l_;  // shorthand
    const packed_v32* H = nullptr;
    std::vector<v32> tmp(16);

    for (size_t b = 0; b < max; ++b) {
      const v8* inb = &in[64 * b];
      for (size_t i = 0; i < 16; ++i) {
        // big-endian mapping of v8[4] into v32.  The first
        // argument of vappend() is the LSB, and thus +3 is
        // the LSB and +0 is the MSB, hence big-endian.
        tmp[i] = L.vappend(L.vappend(inb[4 * i + 3], inb[4 * i + 2]),
                           L.vappend(inb[4 * i + 1], inb[4 * i + 0]));
      }
      if (b == 0) {
        v32 H0[8];
        initial_context(H0);
        assert_transform_block(tmp.data(), H0, bw[b].outw, bw[b].oute,
                               bw[b].outa, bw[b].h1);
      } else {
        assert_transform_block(tmp.data(), H, bw[b].outw, bw[b].oute,
                               bw[b].outa, bw[b].h1);
      }
      H = bw[b].h1;
    }
  }

  /* This method checks that the block witness corresponds to the iterated
     computation of the sha block transform on the prefix || input.
  */
  void assert_message_with_prefix(size_t max, const v8& nb,
                                  const v8 in[/* < 64*max */],
                                  const uint8_t prefix[/* len */], size_t len,
                                  const BlockWitness bw[/*max*/]) const {
    const Logic& L = l_;  // shorthand
    std::vector<v32> tmp(16);

    std::vector<v8> bbuf(64 * max);
    for (size_t i = 0; i < len; ++i) {
      L.bits(8, bbuf[i].data(), prefix[i]);
    }
    for (size_t i = 0; i + len < 64 * max; ++i) {
      bbuf[i + len] = in[i];
    }

    assert_message(max, nb, bbuf.data(), bw);
  }

  /* This method checks if H(in) == target. The method requires that in[]
  contains exactly nb*64 bytes and has been padded according to the SHA256
  specification.
  */
  void assert_message_hash(size_t max, const v8& nb, const v8 in[/* 64*max */],
                           const v256& target,
                           const BlockWitness bw[/*max*/]) const {
    assert_message(max, nb, in, bw);
    assert_hash(max, target, nb, bw);
  }

  // This method checks if H(prefix || in) == target.
  // Since the prefix is hardcoded, the compiler can propagate constants
  // and produce smaller circuits. As above, the method requires that in[]
  // contains exactly nb*64 bytes and has been padded according to the SHA256
  // specification. To use this method, compute the block_witness for the
  // entire message as usual.
  void assert_message_hash_with_prefix(size_t max, const v8& nb,
                                       const v8 in[/* 64*max */],
                                       const uint8_t prefix[/* len */],
                                       size_t len, const v256& target,
                                       const BlockWitness bw[/*max*/]) const {
    assert_message_with_prefix(max, nb, in, prefix, len, bw);
    assert_hash(max, target, nb, bw);
  }

  // Verifies that the nb_th element of the block witness is equal to e.
  // The block witness keeps track of the intermediate output of each
  // block transform.  Therefore, this method can be used to verify that the
  // prover knows a preimage that hashes to the desired e.
  void assert_hash(size_t max, const v256& e, const v8& nb,
                   const BlockWitness bw[/*max*/]) const {
    packed_v32 x[8];
    for (size_t b = 0; b < max; ++b) {
      auto bt = l_.veq(nb, b + 1); /* b is zero-indexed */
      auto ebt = l_.eval(bt);
      for (size_t i = 0; i < 8; ++i) {
        for (size_t k = 0; k < bp_.kNv32Elts; ++k) {
          if (b == 0) {
            x[i][k] = l_.mul(&ebt, bw[b].h1[i][k]);
          } else {
            auto maybe_sha = l_.mul(&ebt, bw[b].h1[i][k]);
            x[i][k] = l_.add(&x[i][k], maybe_sha);
          }
        }
      }
    }

    // Unpack the hash into a v256 in reverse byte-order.
    v256 mm;
    for (size_t j = 0; j < 8; ++j) {
      auto hj = bp_.unpack_v32(x[j]);
      for (size_t k = 0; k < 32; ++k) {
        mm[((7 - j) * 32 + k)] = hj[k];
      }
    }
    l_.vassert_eq(&mm, e);
  }

 private:
  void initial_context(v32 H[8]) const {
    static const uint64_t initial[8] = {0x6a09e667u, 0xbb67ae85u, 0x3c6ef372u,
                                        0xa54ff53au, 0x510e527fu, 0x9b05688cu,
                                        0x1f83d9abu, 0x5be0cd19u};
    for (size_t i = 0; i < 8; i++) {
      H[i] = l_.template vbit<32>(initial[i]);
    }
  }

  v32 Sigma0(const v32& x) const {
    auto x2 = l_.vrotr(x, 2);
    auto x13 = l_.vrotr(x, 13);
    return l_.vxor3(&x2, &x13, l_.vrotr(x, 22));
  }

  v32 Sigma1(const v32& x) const {
    auto x6 = l_.vrotr(x, 6);
    auto x11 = l_.vrotr(x, 11);
    return l_.vxor3(&x6, &x11, l_.vrotr(x, 25));
  }

  v32 sigma0(const v32& x) const {
    auto x7 = l_.vrotr(x, 7);
    auto x18 = l_.vrotr(x, 18);
    return l_.vxor3(&x7, &x18, l_.vshr(x, 3));
  }

  v32 sigma1(const v32& x) const {
    auto x17 = l_.vrotr(x, 17);
    auto x19 = l_.vrotr(x, 19);
    return l_.vxor3(&x17, &x19, l_.vshr(x, 10));
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_FLATSHA256_CIRCUIT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_CIRCUIT_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_CIRCUIT_H_

// Implements a message authentication code in GF 2^k for a 256-bit message.
// The mac key is additively sampled by both the prover and verifier to ensure
// soundness and zk.
//
// The mac is defined as (a_pi+a_v)*x_i = mac_i where (x_1,x_2) are 128-bit
// portions of the hidden message. The verifier need only contribute one
// a_v for all MACs verified in the circuit. The prover needs to commit to
// separate a_p{i} for each portion of each message.
//
// The property that we need from this primitive is as follows:
// Assume the prover has committed to a_pi and x, i.e., fix a_pi, x.
// The probability over the verifier's random a_v that mac(x) = mac_(y)
// if x != y is at most 2^{-128}.

#include <algorithm>
#include <cstddef>

#include "circuits/compiler/compiler.h"
#include "circuits/logic/logic.h"
#include "gf2k/gf2_128.h"

namespace proofs {

static constexpr size_t kMACPluckerBits = 2u;

// MAC: implements a MAC in GF 2^k for a 256-bit message by simulating
// the arithmetic of the GF 2^k field. This implementation commits both
// the prover's a_p key as well as the bits of the message. This allows
// the MAC computation and the equality of the purported message to be verified
// in parallel to reduce depth.
template <class Logic, class BitPlucker>
class MAC {
 public:
  using Field = typename Logic::Field;
  using Elt = typename Field::Elt;
  using EltW = typename Logic::EltW;
  using Nat = typename Field::N;
  using v8 = typename Logic::v8;
  using v128 = typename Logic::v128;
  using v256 = typename Logic::v256;
  using packed_v128 = typename BitPlucker::packed_v128;
  using packed_v256 = typename BitPlucker::packed_v256;

  BitPlucker bp_;

  class Witness {
   public:
    packed_v128 aa_[2];
    packed_v256 xx_;  // The value to be checked

    template <typename T>
    static T packed_input(QuadCircuit<typename Logic::Field>& Q) {
      T r;
      for (size_t i = 0; i < r.size(); ++i) {
        r[i] = Q.input();
      }
      return r;
    }

    void input(const Logic& LC, QuadCircuit<typename Logic::Field>& Q) {
      aa_[0] = packed_input<packed_v128>(Q);
      aa_[1] = packed_input<packed_v128>(Q);
      xx_ = packed_input<packed_v256>(Q);
    }
  };

  explicit MAC(const Logic& lc) : bp_(lc), lc_(lc) {}

  // Verifies a mac on the Field element value against the key (a_p + a_v).
  // This method can only be called when the field is at least 256 bits, e.g.,
  // with F_p256. In other cases, the caller should use a verify_mac method
  // that takes the message in bit-wise form. Additionally, the order parameter
  // is used to ensure that the message does not overflow the field.
  void verify_mac(EltW msg, const v128 mac[/*2*/], const v128& av,
                     const Witness& vw, Nat order) const {
    check(Field::kBits >= 256, "Field::kBits < 256");
    v128 msg2[2];
    unpack_msg(msg2, msg, order, vw);
    assert_mac(mac, av, msg2, vw);
  }

 private:
  // Checks mac[i] = (a_p + a_v)*xi[i] for i=0..1.
  void assert_mac(const v128 mac[/*2*/], const v128& av, const v128 xi[/*2*/],
                  const Witness& vw) const {
    v128 mv;
    for (size_t i = 0; i < 2; ++i) {
      v128 ap = bp_.template unpack<v128, packed_v128>(vw.aa_[i]);
      v128 key = lc_.vxor(&av, ap);
      lc_.gf2_128_mul(mv, key, xi[i]);
      lc_.vassert_eq(&mac[i], mv);
    }
  }

  void unpack_msg(v128 msg[/*2*/], EltW msgw, Nat order,
                  const Witness& vw) const {
    v256 x = bp_.template unpack<v256, packed_v256>(vw.xx_);
    std::copy(x.begin(), x.begin() + 128, msg[0].begin());
    std::copy(x.begin() + 128, x.end(), msg[1].begin());

    // Ensure that the incoming message does not overflow the field.
    v256 bits_n;
    for (size_t i = 0; i < 256; ++i) {
      bits_n[i] = lc_.bit(order.bit(i));
    }
    lc_.assert1(lc_.vlt(&x, bits_n));

    // Verify that the message bits in the witness correspond to msg.
    EltW te = lc_.konst(lc_.zero());
    Elt twok = lc_.one();
    for (size_t i = 0; i < 256; ++i) {
      te = lc_.axpy(&te, twok, lc_.eval(x[i]));
      lc_.f_.add(twok, twok);
    }
    lc_.assert_eq(&te, msgw);
  }

  const Logic& lc_;
};

// Same MAC computation for native GF2_128 field.
template <class Backend, class BitPlucker>
class MACGF2 {
 public:
  using Elt = typename Logic<GF2_128<>, Backend>::Elt;
  using EltW = typename Logic<GF2_128<>, Backend>::EltW;
  using BitW = typename Logic<GF2_128<>, Backend>::BitW;

  // In this specialization, 128 bits are stored in a native EltW.
  using v128 = EltW;

  // Message input types v8, v256 are still encoded bit-wise.
  using v8 = typename Logic<GF2_128<>, Backend>::v8;
  using v256 = typename Logic<GF2_128<>, Backend>::v256;

  explicit MACGF2(const Logic<GF2_128<>, Backend>& lc)
      : lc_(lc) {}
  class Witness {
   public:
    EltW aa_[2];

    void input(const Logic<GF2_128<>, Backend>& lc,
               QuadCircuit<GF2_128<>>& Q) {
      aa_[0] = Q.input();
      aa_[1] = Q.input();
    }
  };

  // Verify a mac on the 256-bit message msg.
  void verify_mac(const EltW mac[/*2*/], const EltW& av, const v256& msg,
                  const Witness& vw) const {
    // Check that mac[i] = (a_p + a_v)*mm[i] for i=0..1.
    for (size_t i = 0; i < 2; ++i) {
      EltW mm = pack(&msg[i * 128]);
      EltW key = lc_.add(&av, vw.aa_[i]);
      EltW got = lc_.mul(&key, mm);
      lc_.assert_eq(&mac[i], got);
    }
  }

 private:
  // Pack a 128-bit message into a GF(2^128) field element.
  EltW pack(const BitW msg[/*128*/]) const {
    Elt alpha = lc_.f_.x();
    Elt xi = lc_.f_.one();
    EltW m = lc_.konst(0);
    for (size_t i = 0; i < 128; ++i) {
      m = lc_.axpy(&m, xi, lc_.eval(msg[i]));
      xi = lc_.mulf(xi, alpha);
    }
    return m;
  }

  const Logic<GF2_128<>, Backend>& lc_;
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_CIRCUIT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_WITNESS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_WITNESS_H_

#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <string>
#include <vector>

#include "arrays/dense.h"
#include "circuits/base64/decode_util.h"
#include "circuits/ecdsa/verify_witness.h"
#include "circuits/jwt/jwt_constants.h"
#include "circuits/logic/bit_plucker_encoder.h"
#include "circuits/sha/flatsha256_witness.h"
#include "util/log.h"

namespace proofs {

/* This struct allows a verifier to express which attribute and value the prover
 * must claim. */
struct OpenedAttribute {
  uint8_t id[32];
  uint8_t value[64];
  size_t id_len, value_len;
};

template <class EC, class ScalarField>
class JWTWitness {
  using Field = typename EC::Field;
  using Elt = typename Field::Elt;
  using Nat = typename Field::N;
  using EcdsaWitness = VerifyWitness3<EC, ScalarField>;
  const EC& ec_;

 public:
  Elt e_, r_, s_;
  EcdsaWitness sig_;

  uint8_t preimage_[64 * kMaxJWTSHABlocks];
  uint8_t e_bits_[256];
  FlatSHA256Witness::BlockWitness sha_bw_[kMaxJWTSHABlocks];
  uint8_t numb_; /* Number of the correct sha block. */
  uint8_t na_;   /* Number of attributes. */
  size_t payload_ind_, payload_len_;
  std::vector<size_t> attr_ind_;
  std::vector<size_t> attr_id_len_;
  std::vector<size_t> attr_value_len_;

  explicit JWTWitness(const EC& ec, const ScalarField& Fn)
      : ec_(ec), sig_(Fn, ec) {}

  void fill_witness(DenseFiller<Field>& filler) const {
    filler.push_back(r_);
    filler.push_back(s_);
    filler.push_back(e_);
    sig_.fill_witness(filler);

    // Write the message.
    for (size_t i = 0; i < 64 * kMaxJWTSHABlocks; ++i) {
      filler.push_back(preimage_[i], 8, ec_.f_);
    }

    for (size_t i = 0; i < 256; ++i) {
      filler.push_back(e_bits_[i], 1, ec_.f_);
    }

    for (size_t j = 0; j < kMaxJWTSHABlocks; ++j) {
      fill_sha(filler, sha_bw_[j]);
    }

    filler.push_back(numb_, 8, ec_.f_);

    for (size_t i = 0; i < na_; ++i) {
      filler.push_back(attr_ind_[i], kJWTIndexBits, ec_.f_);
      filler.push_back(attr_id_len_[i], 8, ec_.f_);
      filler.push_back(attr_value_len_[i], 8, ec_.f_);
    }

    filler.push_back(payload_ind_, kJWTIndexBits, ec_.f_);
    filler.push_back(payload_len_, kJWTIndexBits, ec_.f_);
  }

  void fill_sha(DenseFiller<Field>& filler,
                const FlatSHA256Witness::BlockWitness& bw) const {
    BitPluckerEncoder<Field, kSHAJWTPluckerBits> BPENC(ec_.f_);
    for (size_t k = 0; k < 48; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.outw[k]));
    }
    for (size_t k = 0; k < 64; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.oute[k]));
      filler.push_back(BPENC.mkpacked_v32(bw.outa[k]));
    }
    for (size_t k = 0; k < 8; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.h1[k]));
    }
  }

  // Transform from u32 be (i.e., be[0] is the most significant nibble)
  // into nat form, which requires first converting to le byte order.
  Nat nat_from_u32(const uint32_t be[]) const {
    uint8_t tmp[Nat::kBytes];
    const size_t top = Nat::kBytes / 4;
    for (size_t i = 0; i < Nat::kBytes; ++i) {
      tmp[i] = (be[top - i / 4 - 1] >> ((i % 4) * 8)) & 0xff;
    }
    return Nat::of_bytes(tmp);
  }

  // Transform from u8 be (i.e., be[31] is the most significant byte) into
  // nat form, which requires first converting to le byte order.
  Nat nat_from_be(const uint8_t be[/* Nat::kBytes */]) {
    uint8_t tmp[Nat::kBytes];
    // Transform into byte-wise le representation.
    for (size_t i = 0; i < Nat::kBytes; ++i) {
      tmp[i] = be[Nat::kBytes - i - 1];
    }
    return Nat::of_bytes(tmp);
  }

  bool compute_witness(std::string jwt, Elt pkX, Elt pkY,
                       std::vector<OpenedAttribute> attrs) {
    size_t dot = jwt.find_first_of('.');
    size_t dot2 = jwt.find_first_of('.', dot + 1);
    if (dot == std::string::npos || dot2 == std::string::npos) {
      log(ERROR, "JWT is not in the format of header.payload.signature");
      return false;
    }
    auto hdr = jwt.substr(0, dot);
    auto pld = jwt.substr(dot + 1, dot2 - dot - 1);
    auto rest = jwt.substr(dot2 + 1);
    auto msg = jwt.substr(0, dot2);
    payload_len_ = pld.size();
    payload_ind_ = dot + 1;

    if (payload_len_ > kMaxJWTSHABlocks * 64) {
      log(ERROR, "JWT payload is too large");
      return false;
    }

    size_t tilde = rest.find_first_of('~');
    if (tilde == std::string::npos) {
      log(ERROR, "JWT is not in the format of header.payload.signature~epoch");
      return false;
    }
    auto sig = rest.substr(0, tilde);
    auto claims = rest.substr(tilde + 1);

    FlatSHA256Witness::transform_and_witness_message(
        msg.size(), reinterpret_cast<const uint8_t*>(msg.data()),
        kMaxJWTSHABlocks, numb_, preimage_, sha_bw_);

    Nat ne = nat_from_u32(sha_bw_[numb_ - 1].h1);
    e_ = ec_.f_.to_montgomery(ne);

    std::vector<uint8_t> sigb;
    sigb.reserve(ec_.f_.kBytes * 2);
    if (!base64_decode_url(sig, sigb) || sigb.size() < ec_.f_.kBytes * 2) {
      log(ERROR, "signature is not in the format of base64url");
      return false;
    }
    Nat nr = nat_from_be(&sigb[0]);
    Nat ns = nat_from_be(&sigb[ec_.f_.kBytes]);

    r_ = ec_.f_.to_montgomery(nr);
    s_ = ec_.f_.to_montgomery(ns);
    if (!sig_.compute_witness(pkX, pkY, ne, nr, ns)) {
      log(ERROR, "signature verification failed");
      return false;
    }

    for (size_t i = 0; i < 256; ++i) {
      e_bits_[i] = ne.bit(i);
    }

    // Find the positions of each of the attributes.
    na_ = attrs.size();
    std::vector<uint8_t> payload;
    payload.reserve(pld.size());
    if (!base64_decode_url(pld, payload)) {
      log(ERROR, "JWT payload is not in the format of base64url");
      return false;
    }
    std::string str((const char*)payload.data(), payload.size());
    for (size_t i = 0; i < na_; ++i) {
      size_t ind = str.find((const char*)attrs[i].id, 0, attrs[i].id_len);
      if (ind == std::string::npos) {
        log(ERROR, "Could not find attribute %.*s", attrs[i].id_len,
            attrs[i].id);
        return false;
      }
      size_t vstart = ind + attrs[i].id_len + 3;
      size_t vind =
          str.find((const char*)attrs[i].value, vstart, attrs[i].value_len);
      if (vind == std::string::npos || vind != vstart) {
        log(ERROR, "Could not find attribute value %.*s", attrs[i].value_len,
            attrs[i].value);
        return false;
      }
      attr_ind_.push_back(ind);
      attr_id_len_.push_back(attrs[i].id_len);
      attr_value_len_.push_back(attrs[i].value_len);
    }

    return true;
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_WITNESS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_REVOCATION_WITNESS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_REVOCATION_WITNESS_H_

#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <vector>

#include "arrays/dense.h"
#include "circuits/ecdsa/verify_witness.h"
#include "circuits/logic/bit_plucker_encoder.h"
#include "circuits/mdoc/mdoc_revocation_constants.h"
#include "circuits/sha/flatsha256_witness.h"

namespace proofs {

template <class Field>
typename Field::Elt compute_mdoc_revocation_list_witness(
    typename Field::Elt id, const typename Field::Elt list[], size_t list_size,
    const Field& F) {
  typename Field::Elt prodinv = F.one();
  for (size_t i = 0; i < list_size; ++i) {
    prodinv = F.mulf(prodinv, F.subf(list[i], id));
  }
  F.invert(prodinv);
  return prodinv;
}

template <class EC, class ScalarField>
class MdocRevocationSpanWitness {
  using Field = typename EC::Field;
  using Elt = typename Field::Elt;
  using Nat = typename Field::N;
  using EcdsaWitness = VerifyWitness3<EC, ScalarField>;
  const EC& ec_;

 public:
  Elt e_, r_, s_;
  EcdsaWitness sig_;
  uint8_t preimage_[64 * 2];
  uint8_t id_bits_[256];
  uint8_t e_bits_[256];
  FlatSHA256Witness::BlockWitness sha_bw_[2];

  explicit MdocRevocationSpanWitness(const EC& ec, const ScalarField& Fn)
      : ec_(ec), sig_(Fn, ec) {}

  void fill_witness(DenseFiller<Field>& filler) const {
    filler.push_back(r_);
    filler.push_back(s_);
    filler.push_back(e_);
    sig_.fill_witness(filler);

    // Write the span message.
    for (size_t i = 0; i < 64 * 2; ++i) {
      for (size_t j = 0; j < 8; ++j) {
        filler.push_back((preimage_[i] >> j) & 0x1 ? ec_.f_.one()
                                                   : ec_.f_.zero());
      }
    }

    for (size_t i = 0; i < 256; ++i) {
      filler.push_back(id_bits_[i] ? ec_.f_.one() : ec_.f_.zero());
    }
    for (size_t i = 0; i < 256; ++i) {
      filler.push_back(e_bits_[i] ? ec_.f_.one() : ec_.f_.zero());
    }

    for (size_t j = 0; j < 2; j++) {
      fill_sha(filler, sha_bw_[j]);
    }
  }

  void fill_sha(DenseFiller<Field>& filler,
                const FlatSHA256Witness::BlockWitness& bw) const {
    BitPluckerEncoder<Field, kSHARevocationPluckerBits> BPENC(ec_.f_);
    for (size_t k = 0; k < 48; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.outw[k]));
    }
    for (size_t k = 0; k < 64; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.oute[k]));
      filler.push_back(BPENC.mkpacked_v32(bw.outa[k]));
    }
    for (size_t k = 0; k < 8; ++k) {
      filler.push_back(BPENC.mkpacked_v32(bw.h1[k]));
    }
  }

  bool compute_witness(Elt pkX, Elt pkY, Nat ne, Nat nr, Nat ns, Nat id, Nat ll,
                       Nat rr, uint64_t epoch) {
    e_ = ec_.f_.to_montgomery(ne);
    r_ = ec_.f_.to_montgomery(nr);
    s_ = ec_.f_.to_montgomery(ns);
    sig_.compute_witness(pkX, pkY, ne, nr, ns);

    std::vector<uint8_t> buf;
    for (size_t i = 0; i < 8; ++i) {
      buf.push_back(epoch & 0xff);
      epoch >>= 8;
    }
    uint8_t tmp[Field::kBytes];
    ll.to_bytes(tmp);
    buf.insert(buf.end(), tmp, tmp + Field::kBytes);
    rr.to_bytes(tmp);
    buf.insert(buf.end(), tmp, tmp + Field::kBytes);

    for (size_t i = 0; i < 256; ++i) {
      id_bits_[i] = id.bit(i);
      e_bits_[i] = ne.bit(i);
    }

    uint8_t numb = 0;
    FlatSHA256Witness::transform_and_witness_message(buf.size(), buf.data(), 2,
                                                     numb, preimage_, sha_bw_);

    return true;
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_REVOCATION_WITNESS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_LOGIC_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_LOGIC_H_

#include <stddef.h>

#include <array>
#include <cstdint>
#include <functional>
#include <vector>

#include "algebra/fp_generic.h"
#include "gf2k/gf2_128.h"
#include "util/panic.h"

namespace proofs {
/*
  Arithmetization of boolean logic in a field.
  This class builds logical and arithmetic operations such as add, sub, mul,
  and, or, xor, etc. over bits in the arithmetic circuit model.
  The class utilizes several optimizations, including changing from the {0,1}
  basis to the {-1,1} basis for representing bits.
 */
template <typename Field_, class Backend>
class Logic {
 public:
  using Field = Field_; /* this class export Field, Elt, and EltW */
  using Elt = typename Field::Elt;
  // an "Elt Wire", a wire carrying an Elt.
  using EltW = typename Backend::V;

  const Field& f_;

  explicit Logic(const Backend* bk, const Field& F) : f_(F), bk_(bk) {}

  //------------------------------------------------------------
  // Arithmetic.

  //
  // Re-export field operations
  Elt addf(const Elt& a, const Elt& b) const { return f_.addf(a, b); }
  Elt mulf(const Elt& a, const Elt& b) const { return f_.mulf(a, b); }
  Elt invertf(const Elt& a) const { return f_.invertf(a); }
  Elt negf(const Elt& a) const { return f_.negf(a); }
  Elt zero() const { return f_.zero(); }
  Elt one() const { return f_.one(); }
  Elt mone() const { return f_.mone(); }
  Elt elt(uint64_t a) const { return f_.of_scalar(a); }

  template <size_t N>
  Elt elt(const char (&s)[N]) const {
    return f_.of_string(s);
  }

  // To ensure deterministic behavior, the order of function calls that produce
  // circuit wires must be well-defined at compile time.
  // The C spec leaves certain order of operations unspecified in expressions.
  // One such ambiguity arises in the order of function calls in an argument
  // list. For example, the expression f(creates_wire(x), creates_wire(y))
  // results in an ambiguous order.
  // To help prevent this, all function calls that create wires can have at most
  // one argument that is itself a function. To enforce this property, we
  // require that all but the last argument to a function be a const pointer.

  // Re-export backend operations
  EltW assert0(const EltW& a) const { return bk_->assert0(a); }
  EltW add(const EltW* a, const EltW& b) const { return bk_->add(*a, b); }

  EltW sub(const EltW* a, const EltW& b) const { return bk_->sub(*a, b); }

  EltW mul(const EltW* a, const EltW& b) const { return bk_->mul(*a, b); }
  EltW mul(const Elt& k, const EltW& b) const { return bk_->mul(k, b); }
  EltW mul(const Elt& k, const EltW* a, const EltW& b) const {
    return bk_->mul(k, a, b);
  }

  EltW ax(const Elt& a, const EltW& x) const { return bk_->ax(a, x); }
  EltW axy(const Elt& a, const EltW* x, const EltW& y) const {
    return bk_->axy(a, *x, y);
  }
  EltW axpy(const EltW* y, const Elt& a, const EltW& x) const {
    return bk_->axpy(*y, a, x);
  }
  EltW apy(const EltW& y, const Elt& a) const { return bk_->apy(y, a); }

  EltW konst(const Elt& a) const { return bk_->konst(a); }
  EltW konst(uint64_t a) const { return konst(elt(a)); }

  template <size_t N>
  std::array<EltW, N> konst(const std::array<Elt, N>& a) const {
    std::array<EltW, N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = konst(a[i]);
    }
    return r;
  }

  //------------------------------------------------------------
  // Boolean logic.
  //
  // We map TRUE to one() and FALSE to zero().  We call this convention
  // the "standard basis".
  //
  // However, actual values on wires may use different conventions,
  // e.g. -1 for TRUE and 1 for FALSE.  To keep track of these changes,
  // we represent boolean values as (c0, c1, x) where c0+c1*x is
  // the value in the standard basis.   c0 and c1
  // are compile-time constants that can be manipulated, and
  // x is a runtime value not known in advance.
  //
  // For example, let the "xor basis" denote the mapping FALSE -> 1,
  // TRUE -> -1.  In the xor basis, xor(a,b)=a*b.  The output of the
  // xor gate in the standard basis would be represented as 1/2 + (-1/2)*x
  // where x=a*b is the wire value in the xor basis.

  // a "bit Wire", a wire carrying a bit
  struct BitW {
    Elt c0, c1;
    EltW x;
    BitW() = default;

    // constructor in the standard basis
    explicit BitW(const EltW& bv, const Field& F)
        : BitW(F.zero(), F.one(), bv) {}

    BitW(Elt c0_, Elt c1_, const EltW& x_) : c0(c0_), c1(c1_), x(x_) {}
  };

  // vectors of N bits
  template <size_t N>
  class bitvec : public std::array<BitW, N> {};

  // Common sizes, publicly exported for convenience. The type names are
  // intentionally lower-case to capture the spirit of basic "intx_t" types.
  using v1 = bitvec<1>;
  using v4 = bitvec<4>;
  using v8 = bitvec<8>;
  using v16 = bitvec<16>;
  using v32 = bitvec<32>;
  using v64 = bitvec<64>;
  using v128 = bitvec<128>;
  using v129 = bitvec<129>;
  using v256 = bitvec<256>;

  // Let v(x)=c0+c1*x.  Return a representation of
  // d0+d1*v(x)=(d0+d1*c0)+(d1*c1)*x without changing x.
  // Does not involve the backend at all.
  BitW rebase(const Elt& d0, const Elt& d1, const BitW& v) const {
    return BitW(addf(d0, mulf(d1, v.c0)), mulf(d1, v.c1), v.x);
  }

  EltW eval(const BitW& v) const {
    EltW r = ax(v.c1, v.x);
    if (v.c0 != zero()) {
      auto c0 = konst(v.c0);
      r = add(&c0, r);
    }
    return r;
  }

  // return an EltW which is 0 iff v is 0
  EltW assert0(const BitW& v) const {
    auto e = eval(v);
    return assert0(e);
  }
  // return an EltW which is 0 iff v is 1
  EltW assert1(const BitW& v) const {
    auto e = lnot(v);
    return assert0(e);
  }

  // 0 iff a==b
  EltW assert_eq(const EltW* a, const EltW& b) const {
    return assert0(sub(a, b));
  }
  EltW assert_eq(const BitW* a, const BitW& b) const {
    return assert0(lxor(a, b));
  }
  EltW assert_implies(const BitW* a, const BitW& b) const {
    return assert1(limplies(a, b));
  }

  // special test for asserting that b \in {0,1} (i.e.,
  // not some other field element).
  EltW assert_is_bit(const BitW& b) const {
    // b - b*b
    // Seems to work better than b*(1-b)
    // Equivalent to land(b,lnot(b)) but does not rely
    // on the specific arithmetization.
    auto eb = eval(b);
    return assert_is_bit(eb);
  }
  EltW assert_is_bit(const EltW& v) const {
    auto vvmv = sub(&v, mul(&v, v));
    return assert0(vvmv);
  }

  // bits in their own basis b + 0*1, to allow for some
  // compile-time constant folding
  BitW bit(size_t b) const {
    return BitW((b == 0) ? zero() : one(), zero(), konst(one()));
  }

  void bits(size_t n, BitW a[/*n*/], uint64_t x) const {
    for (size_t i = 0; i < n; ++i) {
      a[i] = bit((x >> i) & 1u);
    }
  }

  // gates
  BitW lnot(const BitW& x) const {
    // lnot() is a pure representation change that does not
    // involve actual circuit gates

    // 1 - x in the standard basis
    return rebase(one(), mone(), x);
  }

  BitW land(const BitW* a, const BitW& b) const {
    // a * b in the standard basis
    return mulv(a, b);
  }

  // special case of product of a logic value by a field
  // element
  EltW lmul(const BitW* a, const EltW& b) const {
    // a * b in the standard basis
    auto ab = mulv(a, BitW(b, f_));
    return eval(ab);
  }
  EltW lmul(const EltW* b, const BitW& a) const { return lmul(&a, *b); }

  BitW lxor(const BitW* a, const BitW& b) const {
    return lxor_aux(*a, b, typename Field::TypeTag());
  }
  BitW lxor(const BitW* a, const BitW* b) const {
    return lxor_aux(*a, *b, typename Field::TypeTag());
  }

  BitW lor(const BitW* a, const BitW& b) const {
    auto na = lnot(*a);
    auto nab = land(&na, lnot(b));
    return lnot(nab);
  }

  // a => b
  BitW limplies(const BitW* a, const BitW& b) const {
    auto na = lnot(*a);
    return lor(&na, b);
  }

  // OR of two quantities known to be mutually exclusive
  BitW lor_exclusive(const BitW* a, const BitW& b) const { return addv(*a, b); }

  BitW lxor3(const BitW* a, const BitW* b, const BitW& c) const {
    BitW p = lxor(a, b);
    return lxor(&p, c);
  }

  // sha256 Ch(): (x & y) ^ (~x & z);
  BitW lCh(const BitW* x, const BitW* y, const BitW& z) const {
    auto xy = land(x, *y);
    auto nx = lnot(*x);
    return lor_exclusive(&xy, land(&nx, z));
  }

  // sha256 Maj(): (x & y) ^ (x & z) ^ (y & z);
  BitW lMaj(const BitW* x, const BitW* y, const BitW& z) const {
    // Interpret as x + y + z >= 2 and compute the carry
    // for an adder in the (p, g) basis
    BitW p = lxor(x, *y);
    BitW g = land(x, *y);
    return lor_exclusive(&g, land(&p, z));
  }

  // mux over logic values
  BitW mux(const BitW* control, const BitW* iftrue, const BitW& iffalse) const {
    auto cif = land(control, *iftrue);
    auto nc = lnot(*control);
    auto ciff = land(&nc, *iffalse);
    return lor_exclusive(&cif, ciff);
  }

  // mux over backend values
  EltW mux(const BitW* control, const EltW* iftrue, const EltW& iffalse) const {
    auto cif = lmul(control, *iftrue);
    auto nc = lnot(*control);
    auto ciff = lmul(&nc, iffalse);
    return add(&cif, ciff);
  }

  // sum_{i0 <= i < i1} f(i)
  EltW add(size_t i0, size_t i1, const std::function<EltW(size_t)>& f) const {
    if (i1 <= i0) {
      return konst(0);
    } else if (i1 == i0 + 1) {
      return f(i0);
    } else {
      size_t im = i0 + (i1 - i0) / 2;
      auto lh = add(i0, im, f);
      auto rh = add(im, i1, f);
      return add(&lh, rh);
    }
  }

  // lor_exclusive_{i0 <= i < i1} f(i)
  BitW lor_exclusive(size_t i0, size_t i1,
                     const std::function<BitW(size_t)>& f) const {
    if (i1 <= i0) {
      return bit(0);
    } else if (i1 == i0 + 1) {
      return f(i0);
    } else {
      size_t im = i0 + (i1 - i0) / 2;
      auto lh = lor_exclusive(i0, im, f);
      auto rh = lor_exclusive(im, i1, f);
      return lor_exclusive(&lh, rh);
    }
  }

  // and_{i0 <= i < i1} f(i)
  BitW land(size_t i0, size_t i1, const std::function<BitW(size_t)>& f) const {
    if (i1 <= i0) {
      return bit(1);
    } else if (i1 == i0 + 1) {
      return f(i0);
    } else {
      size_t im = i0 + (i1 - i0) / 2;
      auto lh = land(i0, im, f);
      auto rh = land(im, i1, f);
      return land(&lh, rh);
    }
  }

  // or_{i0 <= i < i1} f(i)
  BitW lor(size_t i0, size_t i1, const std::function<BitW(size_t)>& f) const {
    if (i1 <= i0) {
      return bit(0);
    } else if (i1 == i0 + 1) {
      return f(i0);
    } else {
      size_t im = i0 + (i1 - i0) / 2;
      auto lh = lor(i0, im, f);
      auto rh = lor(im, i1, f);
      return lor(&lh, rh);
    }
  }

  BitW or_of_and(std::vector<std::vector<BitW>> clauses_of_ands) const {
    std::vector<BitW> ands(clauses_of_ands.size());
    for (size_t i = 0; i < clauses_of_ands.size(); ++i) {
      auto ai = clauses_of_ands[i];
      BitW res = land(0, ai.size(), [&](size_t i) { return ai[i]; });
      ands[i] = res;
    }
    return lor(0, ands.size(), [&](size_t i) { return ands[i]; });
  }

  // prod_{i0 <= i < i1} f(i)
  EltW mul(size_t i0, size_t i1, const std::function<EltW(size_t)>& f) const {
    if (i1 <= i0) {
      return konst(1);
    } else if (i1 == i0 + 1) {
      return f(i0);
    } else {
      size_t im = i0 + (i1 - i0) / 2;
      auto lh = mul(i0, im, f);
      auto rh = mul(im, i1, f);
      return mul(&lh, rh);
    }
  }

  // assert that a + b = c in constant depth
  void assert_sum(size_t w, const BitW c[/*w*/], const BitW a[/*w*/],
                  const BitW b[/*w*/]) const {
    // first step of generic_gp_add(): change the basis from
    // (a, b) to (g, p):
    std::vector<BitW> g(w), p(w), cy(w);
    for (size_t i = 0; i < w; ++i) {
      g[i] = land(&a[i], b[i]);
      p[i] = lxor(&a[i], &b[i]);
    }

    // invert the last step of generic_gp_add(): derive
    // cy[i - 1] (called g[i - 1] there) from
    // c[i] and p[i].
    assert_eq(&c[0], p[0]);
    for (size_t i = 1; i < w; ++i) {
      cy[i - 1] = lxor(&c[i], p[i]);
    }

    // Verify that applying ripple_scan to g[] produces cy[].
    // Note that ripple_scan() operates in-place on g[].  Here, however, g[] is
    // the input to ripple_scan(), and cy[] is the output.
    assert_eq(&cy[0], g[0]);
    for (size_t i = 1; i + 1 < w; ++i) {
      auto cyp = land(&cy[i - 1], p[i]);
      auto g_cyp = lor_exclusive(&g[i], cyp);
      assert_eq(&cy[i], g_cyp);
    }
  }

  // (carry, c) = a + b, returning the carry.
  BitW ripple_carry_add(size_t w, BitW c[/*w*/], const BitW a[/*w*/],
                        const BitW b[/*w*/]) const {
    return generic_gp_add(w, c, a, b, &Logic::ripple_scan);
  }

  // (carry, c) = a - b, returning the carry.
  BitW ripple_carry_sub(size_t w, BitW c[/*w*/], const BitW a[/*w*/],
                        const BitW b[/*w*/]) const {
    return generic_gp_sub(w, c, a, b, &Logic::ripple_scan);
  }

  BitW parallel_prefix_add(size_t w, BitW c[/*w*/], const BitW a[/*w*/],
                           const BitW b[/*w*/]) const {
    return generic_gp_add(w, c, a, b, &Logic::sklansky_scan);
  }

  BitW parallel_prefix_sub(size_t w, BitW c[/*w*/], const BitW a[/*w*/],
                           const BitW b[/*w*/]) const {
    return generic_gp_sub(w, c, a, b, &Logic::sklansky_scan);
  }

  // w x w -> 2w-bit multiplier c = a * b
  void multiplier(size_t w, BitW c[/*2*w*/], const BitW a[/*w*/],
                  const BitW b[/*w*/]) const {
    std::vector<BitW> t(w);
    for (size_t i = 0; i < w; ++i) {
      if (i == 0) {
        for (size_t j = 0; j < w; ++j) {
          c[j] = land(&a[0], b[j]);
        }
        c[w] = bit(0);
      } else {
        for (size_t j = 0; j < w; ++j) {
          t[j] = land(&a[i], b[j]);
        }
        BitW carry = ripple_carry_add(w, c + i, t.data(), c + i);
        c[i + w] = carry;
      }
    }
  }

  // w x w -> 2w-bit polynomial multiplier over gf2.  c(x) = a(x) * b(x)
  void gf2_polynomial_multiplier(size_t w, BitW c[/*2*w*/], const BitW a[/*w*/],
                                 const BitW b[/*w*/]) const {
    std::vector<BitW> t(w);
    for (size_t k = 0; k < 2 * w; ++k) {
      size_t n = 0;
      for (size_t i = 0; i < w; ++i) {
        if (k >= i && k - i < w) {
          t[n++] = land(&a[i], b[k - i]);
        }
      }
      c[k] = parity(0, n, t.data());
    }
  }

  // w x w -> 2w-bit polynomial multiplier over gf2.  c(x) = a(x) * b(x)
  // via the Karatsuba recurrence.  Only works for w = 2^k.
  void gf2_polynomial_multiplier_karat(size_t w, BitW c[/*2*w*/],
                                       const BitW a[/*w*/],
                                       const BitW b[/*w*/]) const {
    check(w == 128 || w == 64 || w < 64, "input length is not a power of 2");
    if (w < 64) {
      gf2_polynomial_multiplier(w, c, a, b);
      return;
    } else {
      // We only run this look on w=128 bits. To support odd w,
      std::vector<BitW> a01(w / 2); /* a0 plus a1 */
      std::vector<BitW> b01(w / 2); /* b0 plus b1 */
      std::vector<BitW> ab01(w);
      std::vector<BitW> a0b0(w);
      std::vector<BitW> a1b1(w);

      for (size_t i = 0; i < w / 2; ++i) {
        a01[i] = lxor(&a[i], a[i + w / 2]);
        b01[i] = lxor(&b[i], b[i + w / 2]);
      }

      gf2_polynomial_multiplier_karat(w / 2, &ab01[0], &a01[0], &b01[0]);
      gf2_polynomial_multiplier_karat(w / 2, &a0b0[0], a, b);
      gf2_polynomial_multiplier_karat(w / 2, &a1b1[0], a + w / 2, b + w / 2);

      for (size_t i = 0; i < w; ++i) {
        ab01[i] = lxor3(&ab01[i], &a0b0[i], a1b1[i]);
      }

      for (size_t i = 0; i < w/2; ++i) {
        c[i] = a0b0[i];
        c[i + w/2] = lxor(&a0b0[i + w/2], ab01[i]);
        c[i + w] = lxor(&ab01[i + w/2], a1b1[i]);
        c[i + 3*w/2] = a1b1[i + w/2];
      }
    }
  }

  // Performs field multiplication in GF2^128 defined by the irreducible
  // x^128 + x^7 + x^2 + x + 1. This routine is generated in a sage script that
  // computes a sparse matrix-vector mult via the powers of x^k mod p(x).
  //
  // def make_mulmod(F, n):
  //   r = F(1)
  //   gen = F.gen()
  //   nl = [[] for _ in range(n)]
  //   terms = 0
  //   for i in range(0, 2*n-1):
  //       for j, var in enumerate(r.polynomial().list()):
  //           if var == 1:
  //               nl[j].append(i)
  //       r = r * gen
  //   print(nl)
  void gf2_128_mul(v128& c, const v128 a, const v128 b) const {
    const std::vector<uint16_t> taps[128] = {
        {0, 128, 249, 254},
        {1, 128, 129, 249, 250, 254},
        {2, 128, 129, 130, 249, 250, 251, 254},
        {3, 129, 130, 131, 250, 251, 252},
        {4, 130, 131, 132, 251, 252, 253},
        {5, 131, 132, 133, 252, 253, 254},
        {6, 132, 133, 134, 253, 254},
        {7, 128, 133, 134, 135, 249},
        {8, 129, 134, 135, 136, 250},
        {9, 130, 135, 136, 137, 251},
        {10, 131, 136, 137, 138, 252},
        {11, 132, 137, 138, 139, 253},
        {12, 133, 138, 139, 140, 254},
        {13, 134, 139, 140, 141},
        {14, 135, 140, 141, 142},
        {15, 136, 141, 142, 143},
        {16, 137, 142, 143, 144},
        {17, 138, 143, 144, 145},
        {18, 139, 144, 145, 146},
        {19, 140, 145, 146, 147},
        {20, 141, 146, 147, 148},
        {21, 142, 147, 148, 149},
        {22, 143, 148, 149, 150},
        {23, 144, 149, 150, 151},
        {24, 145, 150, 151, 152},
        {25, 146, 151, 152, 153},
        {26, 147, 152, 153, 154},
        {27, 148, 153, 154, 155},
        {28, 149, 154, 155, 156},
        {29, 150, 155, 156, 157},
        {30, 151, 156, 157, 158},
        {31, 152, 157, 158, 159},
        {32, 153, 158, 159, 160},
        {33, 154, 159, 160, 161},
        {34, 155, 160, 161, 162},
        {35, 156, 161, 162, 163},
        {36, 157, 162, 163, 164},
        {37, 158, 163, 164, 165},
        {38, 159, 164, 165, 166},
        {39, 160, 165, 166, 167},
        {40, 161, 166, 167, 168},
        {41, 162, 167, 168, 169},
        {42, 163, 168, 169, 170},
        {43, 164, 169, 170, 171},
        {44, 165, 170, 171, 172},
        {45, 166, 171, 172, 173},
        {46, 167, 172, 173, 174},
        {47, 168, 173, 174, 175},
        {48, 169, 174, 175, 176},
        {49, 170, 175, 176, 177},
        {50, 171, 176, 177, 178},
        {51, 172, 177, 178, 179},
        {52, 173, 178, 179, 180},
        {53, 174, 179, 180, 181},
        {54, 175, 180, 181, 182},
        {55, 176, 181, 182, 183},
        {56, 177, 182, 183, 184},
        {57, 178, 183, 184, 185},
        {58, 179, 184, 185, 186},
        {59, 180, 185, 186, 187},
        {60, 181, 186, 187, 188},
        {61, 182, 187, 188, 189},
        {62, 183, 188, 189, 190},
        {63, 184, 189, 190, 191},
        {64, 185, 190, 191, 192},
        {65, 186, 191, 192, 193},
        {66, 187, 192, 193, 194},
        {67, 188, 193, 194, 195},
        {68, 189, 194, 195, 196},
        {69, 190, 195, 196, 197},
        {70, 191, 196, 197, 198},
        {71, 192, 197, 198, 199},
        {72, 193, 198, 199, 200},
        {73, 194, 199, 200, 201},
        {74, 195, 200, 201, 202},
        {75, 196, 201, 202, 203},
        {76, 197, 202, 203, 204},
        {77, 198, 203, 204, 205},
        {78, 199, 204, 205, 206},
        {79, 200, 205, 206, 207},
        {80, 201, 206, 207, 208},
        {81, 202, 207, 208, 209},
        {82, 203, 208, 209, 210},
        {83, 204, 209, 210, 211},
        {84, 205, 210, 211, 212},
        {85, 206, 211, 212, 213},
        {86, 207, 212, 213, 214},
        {87, 208, 213, 214, 215},
        {88, 209, 214, 215, 216},
        {89, 210, 215, 216, 217},
        {90, 211, 216, 217, 218},
        {91, 212, 217, 218, 219},
        {92, 213, 218, 219, 220},
        {93, 214, 219, 220, 221},
        {94, 215, 220, 221, 222},
        {95, 216, 221, 222, 223},
        {96, 217, 222, 223, 224},
        {97, 218, 223, 224, 225},
        {98, 219, 224, 225, 226},
        {99, 220, 225, 226, 227},
        {100, 221, 226, 227, 228},
        {101, 222, 227, 228, 229},
        {102, 223, 228, 229, 230},
        {103, 224, 229, 230, 231},
        {104, 225, 230, 231, 232},
        {105, 226, 231, 232, 233},
        {106, 227, 232, 233, 234},
        {107, 228, 233, 234, 235},
        {108, 229, 234, 235, 236},
        {109, 230, 235, 236, 237},
        {110, 231, 236, 237, 238},
        {111, 232, 237, 238, 239},
        {112, 233, 238, 239, 240},
        {113, 234, 239, 240, 241},
        {114, 235, 240, 241, 242},
        {115, 236, 241, 242, 243},
        {116, 237, 242, 243, 244},
        {117, 238, 243, 244, 245},
        {118, 239, 244, 245, 246},
        {119, 240, 245, 246, 247},
        {120, 241, 246, 247, 248},
        {121, 242, 247, 248, 249},
        {122, 243, 248, 249, 250},
        {123, 244, 249, 250, 251},
        {124, 245, 250, 251, 252},
        {125, 246, 251, 252, 253},
        {126, 247, 252, 253, 254},
        {127, 248, 253, 254},
    };
    gf2k_mul(c.data(), a.data(), b.data(), taps, 128);
  }

  // Performs field multiplication in GF2^k using a sparse matrix datastructure.
  void gf2k_mul(BitW c[/*w*/], const BitW a[/*w*/], const BitW b[/*w*/],
                const std::vector<uint16_t> M[], size_t w) const {
    std::vector<BitW> t(w * 2);
    gf2_polynomial_multiplier_karat(w, t.data(), a, b);

    std::vector<BitW> tmp(w);
    for (size_t i = 0; i < w; ++i) {
      size_t n = 0;
      for (auto ti : M[i]) {
        tmp[n++] = t[ti];
      }
      c[i] = parity(0, n, tmp.data());
    }
  }

  // a == 0
  BitW eq0(size_t w, const BitW a[/*w*/]) const { return eq0(0, w, a); }

  // a == b
  BitW eq(size_t w, const BitW a[/*w*/], const BitW b[/*w*/]) const {
    return eq_reduce(0, w, a, b);
  }

  // a < b.
  // Specialization of the subtractor for the case (a - b) < 0
  BitW lt(size_t w, const BitW a[/*w*/], const BitW b[/*w*/]) const {
    if (w == 0) {
      return bit(0);
    } else {
      BitW xeq, xlt;
      lt_reduce(0, w, &xeq, &xlt, a, b);
      return xlt;
    }
  }

  // a <= b
  BitW leq(size_t w, const BitW a[/*w*/], const BitW b[/*w*/]) const {
    auto blt = lt(w, b, a);
    return lnot(blt);
  }

  // Parallel prefix of various kinds
  template <class T>
  void scan(const std::function<void(T*, const T&, const T&)>& op, T x[],
            size_t i0, size_t i1, bool backward = false) const {
    // generic Sklansky scan
    if (i1 - i0 > 1) {
      size_t im = i0 + (i1 - i0) / 2;
      scan(op, x, i0, im, backward);
      scan(op, x, im, i1, backward);
      if (backward) {
        for (size_t i = i0; i < im; ++i) {
          op(&x[i], x[i], x[im]);
        }
      } else {
        for (size_t i = im; i < i1; ++i) {
          op(&x[i], x[im - 1], x[i]);
        }
      }
    }
  }

  void scan_and(BitW x[], size_t i0, size_t i1, bool backward = false) const {
    scan<BitW>(
        [&](BitW* out, const BitW& l, const BitW& r) { *out = land(&l, r); }, x,
        i0, i1, backward);
  }

  void scan_or(BitW x[], size_t i0, size_t i1, bool backward = false) const {
    scan<BitW>(
        [&](BitW* out, const BitW& l, const BitW& r) { *out = lor(&l, r); }, x,
        i0, i1, backward);
  }

  void scan_xor(BitW x[], size_t i0, size_t i1, bool backward = false) const {
    scan<BitW>(
        [&](BitW* out, const BitW& l, const BitW& r) { *out = lxor(&l, r); }, x,
        i0, i1, backward);
  }

  template <size_t I0, size_t I1, size_t N>
  bitvec<I1 - I0> slice(const bitvec<N>& a) const {
    bitvec<I1 - I0> r;
    for (size_t i = I0; i < I1; ++i) {
      r[i - I0] = a[i];
    }
    return r;
  }

  // Little-endian append of A and B.  A[0] is the LSB, B starts at
  // position [NA].
  template <size_t NA, size_t NB>
  bitvec<NA + NB> vappend(const bitvec<NA>& a, const bitvec<NB>& b) const {
    bitvec<NA + NB> r;
    for (size_t i = 0; i < NA; ++i) {
      r[i] = a[i];
    }
    for (size_t i = 0; i < NB; ++i) {
      r[i + NA] = b[i];
    }
    return r;
  }

  template <size_t N>
  bool vequal(const bitvec<N>* a, const bitvec<N>& b) const {
    for (size_t i = 0; i < N; ++i) {
      auto eai = eval((*a)[i]);
      auto ebi = eval(b[i]);
      if (eai != ebi) return false;
    }
    return true;
  }

  template <size_t N>
  bitvec<N> vbit(uint64_t x) const {
    bitvec<N> r;
    bits(N, r.data(), x);
    return r;
  }

  // shorthands for the silly "template" notation
  v8 vbit8(uint64_t x) const { return vbit<8>(x); }
  v32 vbit32(uint64_t x) const { return vbit<32>(x); }

  template <size_t N>
  bitvec<N> vnot(const bitvec<N>& x) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = lnot(x[i]);
    }
    return r;
  }

  template <size_t N>
  bitvec<N> vand(const bitvec<N>* a, const bitvec<N>& b) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = land(&(*a)[i], b[i]);
    }
    return r;
  }

  template <size_t N>
  bitvec<N> vand(const BitW* a, const bitvec<N>& b) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = land(a, b[i]);
    }
    return r;
  }

  template <size_t N>
  bitvec<N> vor(const bitvec<N>* a, const bitvec<N>& b) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = lor(&(*a)[i], b[i]);
    }
    return r;
  }
  template <size_t N>
  bitvec<N> vor_exclusive(const bitvec<N>* a, const bitvec<N>& b) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = lor_exclusive(&(*a)[i], b[i]);
    }
    return r;
  }
  template <size_t N>
  bitvec<N> vxor(const bitvec<N>* a, const bitvec<N>& b) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = lxor(&(*a)[i], b[i]);
    }
    return r;
  }

  template <size_t N>
  bitvec<N> vCh(const bitvec<N>* x, const bitvec<N>* y,
                const bitvec<N>& z) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = lCh(&(*x)[i], &(*y)[i], z[i]);
    }
    return r;
  }
  template <size_t N>
  bitvec<N> vMaj(const bitvec<N>* x, const bitvec<N>* y,
                 const bitvec<N>& z) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = lMaj(&(*x)[i], &(*y)[i], z[i]);
    }
    return r;
  }

  template <size_t N>
  bitvec<N> vxor3(const bitvec<N>* x, const bitvec<N>* y,
                  const bitvec<N>& z) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = lxor3(&(*x)[i], &(*y)[i], z[i]);
    }
    return r;
  }

  template <size_t N>
  bitvec<N> vshr(const bitvec<N>& a, size_t shift, size_t b = 0) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      if (i + shift < N) {
        r[i] = a[i + shift];
      } else {
        r[i] = bit(b);
      }
    }
    return r;
  }

  template <size_t N>
  bitvec<N> vshl(const bitvec<N>& a, size_t shift, size_t b = 0) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      if (i >= shift) {
        r[i] = a[i - shift];
      } else {
        r[i] = bit(b);
      }
    }
    return r;
  }

  template <size_t N>
  bitvec<N> vrotr(const bitvec<N>& a, size_t b) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = a[(i + b) % N];
    }
    return r;
  }

  template <size_t N>
  bitvec<N> vrotl(const bitvec<N>& a, size_t b) const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[(i + b) % N] = a[i];
    }
    return r;
  }

  template <size_t N>
  bitvec<N> vadd(const bitvec<N>& a, const bitvec<N>& b) const {
    bitvec<N> r;
    (void)parallel_prefix_add(N, &r[0], &a[0], &b[0]);
    return r;
  }
  template <size_t N>
  bitvec<N> vadd(const bitvec<N>& a, uint64_t val) const {
    return vadd(a, vbit<N>(val));
  }

  template <size_t N>
  BitW veq(const bitvec<N>& a, const bitvec<N>& b) const {
    return eq(N, a.data(), b.data());
  }
  template <size_t N>
  BitW veq(const bitvec<N>& a, uint64_t val) const {
    auto v = vbit<N>(val);
    return veq(a, v);
  }
  template <size_t N>
  BitW vlt(const bitvec<N>* a, const bitvec<N>& b) const {
    return lt(N, (*a).data(), b.data());
  }
  template <size_t N>
  BitW vlt(const bitvec<N>& a, uint64_t val) const {
    auto v = vbit<N>(val);
    return vlt(&a, v);
  }
  template <size_t N>
  BitW vlt(uint64_t a, const bitvec<N>& b) const {
    auto va = vbit<N>(a);
    return vlt(&va, b);
  }
  template <size_t N>
  BitW vleq(const bitvec<N>* a, const bitvec<N>& b) const {
    return leq(N, (*a).data(), b.data());
  }
  template <size_t N>
  BitW vleq(const bitvec<N>& a, uint64_t val) const {
    auto v = vbit<N>(val);
    return vleq(&a, v);
  }

  // (a ^ val) & mask == 0
  template <size_t N>
  BitW veqmask(const bitvec<N>* a, uint64_t mask, const bitvec<N>& val) const {
    auto r = vxor(a, val);
    size_t n = pack(mask, N, &r[0]);
    return eq0(0, n, &r[0]);
  }

  template <size_t N>
  BitW veqmask(const bitvec<N>& a, uint64_t mask, uint64_t val) const {
    auto v = vbit<N>(val);
    return veqmask(&a, mask, v);
  }

  // I/O.  This is a hack which only works if the backend supports
  // bk_->{input,output}.  Because C++ templates are lazily expanded,
  // this class compiles even with backends that do not support I/O,
  // as long as you don't expand vinput(), voutput().
  BitW input() const { return BitW(bk_->input(), f_); }
  void output(const BitW& x, size_t i) const { bk_->output(eval(x), i); }
  size_t wire_id(const BitW& v) const { return bk_->wire_id(v.x); }
  size_t wire_id(const EltW& x) const { return bk_->wire_id(x); }

  template <size_t N>
  bitvec<N> vinput() const {
    bitvec<N> r;
    for (size_t i = 0; i < N; ++i) {
      r[i] = input();
    }
    return r;
  }

  template <size_t N>
  void voutput(const bitvec<N>& x, size_t i0) const {
    for (size_t i = 0; i < N; ++i) {
      output(x[i], i + i0);
    }
  }

  template <size_t N>
  void vassert0(const bitvec<N>& x) const {
    for (size_t i = 0; i < N; ++i) {
      (void)assert0(x[i]);
    }
  }

  template <size_t N>
  void vassert_eq(const bitvec<N>* x, const bitvec<N>& y) const {
    for (size_t i = 0; i < N; ++i) {
      (void)assert_eq(&(*x)[i], y[i]);
    }
  }

  template <size_t N>
  void vassert_eq(const bitvec<N>& x, uint64_t y) const {
    auto v = vbit<N>(y);
    vassert_eq(&x, v);
  }

  template <size_t N>
  void vassert_is_bit(const bitvec<N>& a) const {
    for (size_t i = 0; i < N; ++i) {
      (void)assert_is_bit(a[i]);
    }
  }

 private:
  // return one quad gate for the product eval(a)*eval(b),
  // optimizing some "obvious" cases.
  BitW mulv(const BitW* a, const BitW& b) const {
    if (a->c1 == zero()) {
      return rebase(zero(), a->c0, b);
    } else if (b.c1 == zero()) {
      return mulv(&b, *a);
    } else {
      // Avoid creating the intermediate term 1 * a.x * b.x which is
      // likely a useless node.  Moreover, two nodes (k1 * a.x * b.x)
      // and (k2 * a.x * b.x) will detect the common subexpression
      // (a.x * b.x), which will confusingly increment the
      // common-subexpression counter.
      EltW x = axy(mulf(a->c1, b.c1), &a->x, b.x);
      x = axpy(&x, mulf(a->c0, b.c1), b.x);
      x = axpy(&x, mulf(a->c1, b.c0), a->x);
      x = apy(x, mulf(a->c0, b.c0));
      return BitW(x, f_);
    }
  }

  BitW addv(const BitW& a, const BitW& b) const {
    if (a.c1 == zero()) {
      return BitW(addf(a.c0, b.c0), b.c1, b.x);
    } else if (b.c1 == zero()) {
      return addv(b, a);
    } else {
      EltW x = ax(a.c1, a.x);
      auto axb = ax(b.c1, b.x);
      x = add(&x, axb);
      x = apy(x, addf(a.c0, b.c0));
      return BitW(x, f_);
    }
  }

  BitW lxor_aux(const BitW& a, const BitW& b, PrimeFieldTypeTag tt) const {
    // a * b in the xor basis TRUE -> -1, FALSE -> 1
    // map a, b from standard basis to xor basis
    Elt mtwo = f_.negf(f_.two());
    Elt half = f_.half();
    Elt mhalf = f_.negf(half);

    BitW a1 = rebase(one(), mtwo, a);
    BitW b1 = rebase(one(), mtwo, b);
    BitW p = mulv(&a1, b1);
    return rebase(half, mhalf, p);
  }
  BitW lxor_aux(const BitW& a, const BitW& b, BinaryFieldTypeTag tt) const {
    return addv(a, b);
  }

  size_t pack(uint64_t mask, size_t n, BitW a[/*n*/]) const {
    size_t j = 0;
    for (size_t i = 0; i < n; ++i) {
      if (mask & 1) {
        a[j++] = a[i];
      }
      mask >>= 1;
    }
    return j;
  }

  // carry-propagation equations
  // (g0, p0) + (g1, p1) = (g1 | (g0 & p1), p0 & p1)
  // Accumulate in-place into (g1, p1).
  //
  // We use the property that g1 and p1 are mutually exclusive (g1&p1
  // is false), and therefore g1 and (g0 & p1) are also mutually
  // exclusive.
  void gp_reduce(const BitW& g0, const BitW& p0, BitW* g1, BitW* p1) const {
    auto g0p1 = land(&g0, *p1);
    *g1 = lor_exclusive(g1, g0p1);
    *p1 = land(&p0, *p1);
  }

  // ripple carry propagation
  void ripple_scan(std::vector<BitW>& g, std::vector<BitW>& p, size_t i0,
                   size_t i1) const {
    for (size_t i = i0 + 1; i < i1; ++i) {
      gp_reduce(g[i - 1], p[i - 1], &g[i], &p[i]);
    }
  }

  // parallel-prefix carry propagation, Sklansky-style [1960]
  void sklansky_scan(std::vector<BitW>& g, std::vector<BitW>& p, size_t i0,
                     size_t i1) const {
    if (i1 - i0 > 1) {
      size_t im = i0 + (i1 - i0) / 2;
      sklansky_scan(g, p, i0, im);
      sklansky_scan(g, p, im, i1);
      for (size_t i = im; i < i1; ++i) {
        gp_reduce(g[im - 1], p[im - 1], &g[i], &p[i]);
      }
    }
  }

  // generic add in generate/propagate form, parametrized
  // by the scan primitive.
  //
  // (carry, c) = a + b
  BitW generic_gp_add(size_t w, BitW c[/*w*/], const BitW a[/*w*/],
                      const BitW b[/*w*/],
                      void (Logic::*scan)(std::vector<BitW>& /*g*/,
                                          std::vector<BitW>& /*p*/,
                                          size_t /*i0*/, size_t /*i1*/)
                          const) const {
    if (w == 0) {
      return bit(0);
    } else {
      std::vector<BitW> g(w), p(w);
      for (size_t i = 0; i < w; ++i) {
        g[i] = land(&a[i], b[i]);
        p[i] = lxor(&a[i], b[i]);
        c[i] = p[i];
      }
      (this->*scan)(g, p, 0, w);
      for (size_t i = 1; i < w; ++i) {
        c[i] = lxor(&c[i], g[i - 1]);
      }
      return g[w - 1];
    }
  }

  BitW generic_gp_sub(size_t w, BitW c[/*w*/], const BitW a[/*w*/],
                      const BitW b[/*w*/],
                      void (Logic::*scan)(std::vector<BitW>& /*g*/,
                                          std::vector<BitW>& /*p*/,
                                          size_t /*i0*/, size_t /*i1*/)
                          const) const {
    // implement as ~(~a + b)
    std::vector<BitW> t(w);
    for (size_t j = 0; j < w; ++j) {
      t[j] = lnot(a[j]);
    }
    BitW carry = generic_gp_add(w, c, t.data(), b, scan);
    for (size_t j = 0; j < w; ++j) {
      c[j] = lnot(c[j]);
    }
    return carry;
  }

  // Recursion for the a < b comparison.
  // Let a = (a1, a0) and b = (b1, b0).  Then:
  //
  // a == b   iff a1 == b1 && a0 == b0
  // a < b    iff a1 < b1 || (a1 == b1 && a0 < b0)
  void lt_reduce(size_t i0, size_t i1, BitW* xeq, BitW* xlt,
                 const BitW a[/*w*/], const BitW b[/*w*/]) const {
    if (i1 - i0 > 1) {
      BitW eq0, eq1, lt0, lt1;
      size_t im = i0 + (i1 - i0) / 2;
      lt_reduce(i0, im, &eq0, &lt0, a, b);
      lt_reduce(im, i1, &eq1, &lt1, a, b);
      *xeq = land(&eq1, eq0);
      auto lt0_and_eq1 = land(&eq1, lt0);
      *xlt = lor_exclusive(&lt1, lt0_and_eq1);
    } else {
      auto axb = lxor(&a[i0], b[i0]);
      *xeq = lnot(axb);
      auto na = lnot(a[i0]);
      *xlt = land(&na, b[i0]);
    }
  }

  BitW parity(size_t i0, size_t i1, const BitW a[]) const {
    if (i1 <= i0) {
      return bit(0);
    } else if (i1 == i0 + 1) {
      return a[i0];
    } else {
      size_t im = i0 + (i1 - i0) / 2;
      auto lp = parity(i0, im, a);
      auto rp = parity(im, i1, a);
      return lxor(&lp, rp);
    }
  }

  BitW eq0(size_t i0, size_t i1, const BitW a[]) const {
    if (i1 <= i0) {
      return bit(1);
    } else if (i1 == i0 + 1) {
      return lnot(a[i0]);
    } else {
      size_t im = i0 + (i1 - i0) / 2;
      auto le = eq0(i0, im, a);
      auto re = eq0(im, i1, a);
      return land(&le, re);
    }
  }

  BitW eq_reduce(size_t i0, size_t i1, const BitW a[], const BitW b[]) const {
    if (i1 <= i0) {
      return bit(1);
    } else if (i1 == i0 + 1) {
      return lnot(lxor(&a[i0], b[i0]));
    } else {
      size_t im = i0 + (i1 - i0) / 2;
      auto le = eq_reduce(i0, im, a, b);
      auto re = eq_reduce(im, i1, a, b);
      return land(&le, re);
    }
  }

  const Backend* bk_;
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_LOGIC_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_COMPILER_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_COMPILER_H_

#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <memory>
#include <vector>

#include "algebra/hash.h"
#include "circuits/compiler/circuit_id.h"
#include "circuits/compiler/node.h"
#include "circuits/compiler/pdqhash.h"
#include "circuits/compiler/schedule.h"
#include "sumcheck/circuit.h"
#include "sumcheck/quad.h"
#include "util/panic.h"

namespace proofs {
/*
QuadCircuit contains methods that facilitate defining circuits used to
express predicates that are to be proven or verified. This class allows one
to use basic arithmetic circuit operations (add, mul, input, assert0, ...)
to define the circuit on a set of abstract wire labels.

The "mkcircuit" compiler method than optimizes the circuit by applying all of
the basic tricks of constant propagation, common sub-expression elimination,
squashing layers into as few as possible, and grouping terms into quads.

Quads are a new form of gate (in contrast to the add and mul gates in most
sumcheck proof systems). Quads represent a "sum of quadratic terms" where
each term is w_l * w_r * v for two wire labels and a constant v.
*/
template <class Field>
class QuadCircuit {
 public:
  using Elt = typename Field::Elt;
  using nodeinfo = NodeInfoF<Field>;
  using node = NodeF<Field>;
  using size_t_for_storage = term::size_t_for_storage;
  using quad_corner_t = typename Quad<Field>::quad_corner_t;

  const Field& f_;

 public:
  // Variables for informational purposes:
  size_t ninput_;
  size_t npub_input_;         // number of public inputs, index of 1st private
  size_t subfield_boundary_;  // least wire not known to be in the subfield
  size_t noutput_;

  // set by the algebraic simplifiers in this file
  size_t depth_;
  size_t nwires_cse_eliminated_;
  size_t nwires_not_needed_;

  // set by the scheduler
  size_t nwires_;
  size_t nquad_terms_;
  size_t nwires_overhead_;

  explicit QuadCircuit(const Field& f)
      : f_(f),
        ninput_(0),
        npub_input_(0),
        subfield_boundary_(0),
        noutput_(0),
        depth_(0),
        nwires_cse_eliminated_(0),
        nwires_not_needed_(0),
        nwires_(-1),  // undefined until set in mkcircuit()
        nquad_terms_(-1),
        nwires_overhead_(-1) {
    // make sure that Elt(0) is represented as index 0 in the constant
    // table.
    size_t ki0 = kstore(f.zero());
    proofs::check(ki0 == 0, "ki0 == 0");
    size_t ki1 = kstore(f.one());
    proofs::check(ki1 == 1, "ki1 == 1");

    // make sure node 0 exists, carrying input[0] = F.one()
    input();
  }

  // Produce a linear term 1 * op0 that the compiler will not
  // attempt to optimize to op0.  The reason for this function
  // is to implement linear terms such as a*x in the quadratic form
  // a*x+b*x*y.  Left to its own devices, the compiler peeks into x,
  // and if x=k*z*w, it produces a term (a*k)*z*w in the previous
  // layer, possibly destroying common subexpressions.  linear(op)
  // introduces an explicit multiplication by wire 0, which the
  // compiler does not attempt to optimize away.
  size_t linear(size_t op0) { return mul(0, op0); }
  size_t linear(const Elt& k, size_t op0) { return mul(k, 0, op0); }

  size_t mul(const Elt& k, size_t op) {
    if (k == f_.zero()) {
      return konst(k);
    } else if (k == f_.one() || nodes_[op].zero()) {
      return op;
    } else {
      return push_node(scale(k, op));
    }
  }

  size_t mul(size_t op0, size_t op1) { return mul(f_.one(), op0, op1); }

  size_t mul(const Elt& k, size_t op0, size_t op1) {
    const auto& n0 = nodes_[op0];
    const auto& n1 = nodes_[op1];

    if (n0.zero()) {
      return op0;
    } else if (n0.constant()) {
      // k * (k1 * op1) -> (k * k1) * op1
      return mul(f_.mulf(k, kload(n0.terms[0].ki)), op1);
    } else if (n0.linearp()) {
      // k * ((k1 * op0) * op1) -> (k * k1) * op0 * op1
      return mul(f_.mulf(k, kload(n0.terms[0].ki)), n0.terms[0].op1, op1);
    } else if (n1.zero() || n1.constant() || n1.linearp()) {
      return mul(k, op1, op0);
    } else {
      // general term k * op0 * op1
      return push_node(node(kstore(k), op0, op1));
    }
  }

  size_t add(size_t op0, size_t op1) {
    const auto& n0 = nodes_[op0];
    const auto& n1 = nodes_[op1];

    if (n0.zero()) {
      return op1;
    } else if (n1.zero()) {
      return op0;
    } else {
      // If the two addends are of different depth, do not merge
      // them, which is accomplished by multiplying the shallower
      // node by 1 and treating it as a single term of the final
      // sum.
      //
      // Like many other "optimizations", this is a heuristic
      // that may or may not work, but it seems to be uniformly
      // beneficial or at least not harmful for all our circuits
      // as of 2023-11-15.
      if (n0.info.depth < n1.info.depth) {
        op0 = linear(op0);
      } else if (n1.info.depth < n0.info.depth) {
        op1 = linear(op1);
      }
      return push_node(merge(op0, op1));
    }
  }
  size_t sub(size_t op0, size_t op1) { return add(op0, mul(f_.mone(), op1)); }

  size_t konst(const Elt& k) { return push_node(node(kstore(k), 0, 0)); }

  // Generate a special node that asserts that op == 0.
  // The node has the form 0*(1*op), which does not normally
  // appear in circuits.
  size_t assert0(size_t op) {
    const node* n = &nodes_[op];
    if (n->zero()) {
      // Identically zero, so nothing to generate.
      // More importantly, we cannot multiply OP by 1,
      // since OP doesn't really exist.
      return op;
    } else if (n->linearp()) {
      // n = k * (1 * op1).
      //
      // Reduce to assert0(op1), but handle the screw case k==0,
      // which shouldn't happen but just in case...
      if (n->terms[0].ki == 0) {
        return op;
      } else {
        return assert0(n->terms[0].op1);
      }
    } else {
      typename term::assert0_type_hack hack;
      std::vector<term> terms;
      terms.push_back(term(op, hack));
      size_t n1 = push_node(node(terms));
      nodes_[n1].info.is_assert0 = true;
      return n1;
    }
  }

  // Wrappers to avoid creating unnecessary wires.  The
  // compiler will discard them anyway, but they still take
  // time and space.
  size_t axpy(size_t y, const Elt& a, size_t x) {
    if (a == f_.zero()) {
      return y;
    }
    return add(y, linear(a, x));
  }
  size_t apy(size_t y, const Elt& a) {
    if (a == f_.zero()) {
      return y;
    }
    return add(y, konst(a));
  }

  size_t input() { return push_node(node(quad_corner_t(ninput_++))); }

  // This function demarcates the end of the public inputs and beginning of
  // private inputs. It can only be called once.
  void private_input() {
    proofs::check(
        npub_input_ == 0,
        "private_input can only be called once after setting public inputs");
    npub_input_ = ninput_;
  }

  // This function demarcates the end of the private inputs in the
  // subfield and beginning of the full-field private inputs. It can
  // only be called once.
  void begin_full_field() {
    proofs::check(subfield_boundary_ == 0,
                  "begin_full_field() can only be called once");
    subfield_boundary_ = ninput_;
  }

  size_t ninput() const { return ninput_; }

  void output(size_t n, size_t wire_id) {
    output_internal(n, quad_corner_t(wire_id));
  }

  std::unique_ptr<Circuit<Field>> mkcircuit(size_t nc) {
    size_t depth_ub = compute_depth_ub();
    fixup_last_layer_assertions(depth_ub);
    compute_needed(depth_ub);

    Scheduler<Field> sched(nodes_, f_);
    std::unique_ptr<Circuit<Field>> c =
        sched.mkcircuit(constants_, depth_ub, nc);

    // re-export the scheduler telemetry
    nwires_ = sched.nwires_;
    nquad_terms_ = sched.nquad_terms_;
    nwires_overhead_ = sched.nwires_overhead_;

    c->ninputs = ninput();
    c->npub_in = npub_input_;
    c->subfield_boundary = subfield_boundary_;

    circuit_id(c->id, *c, f_);
    return c;
  }

 private:
  void output_internal(size_t n, quad_corner_t wire_id) {
    nodes_[n].info.is_output = true;
    nodes_[n].info.desired_wire_id_for_output = wire_id;
    noutput_++;
  }

  size_t push_node(node n) {
    // common-subexpression elimination: if we have already seen a
    // node equal to n, return that node.
    uint64_t d = n.hash();

    auto pred = [&](PdqHash::value_t op) { return n == nodes_[op]; };
    if (size_t op = cse_.find(d, pred); op != PdqHash::kNil) {
      // do not linear terms as eliminated by the CSE, since they are
      // likely placeholder nodes absorbed by the next layer.
      if (!n.linearp()) {
        ++nwires_cse_eliminated_;
      }
      return op;
    }

    // compute the node depth, which has been so far uninitialized
    n.info.depth = 0;
    for (const auto& t : n.terms) {
      n.info.depth = std::max<size_t>(
          n.info.depth, 1 + std::max<size_t>(nodes_[t.op0].info.depth,
                                             nodes_[t.op1].info.depth));
    }

    size_t nid = nodes_.size();
    nodes_.push_back(n);

    // record NID into the common-subexpression elimination table
    cse_.insert(d, nid);

    return nid;
  }

  node materialize_input(size_t op) {
    if (nodes_[op].info.is_input) {
      return node(/*kstore(f.one())=*/1, 0, op);
    } else {
      return /*a copy of*/ nodes_[op];
    }
  }

  node scale(const Elt& k, size_t op) {
    node n = materialize_input(op);
    for (auto& t : n.terms) {
      t.ki = kstore(f_.mulf(kload(t.ki), k));
    }
    return n;
  }

  void push_back_unless_zero(std::vector<term>& terms, const term& t) const {
    if (t.ki != 0) {
      terms.push_back(t);
    }
  }

  node merge(size_t op0, size_t op1) {
    const node n0 = materialize_input(op0);
    const node n1 = materialize_input(op1);
    const std::vector<term>& t0 = n0.terms;
    const std::vector<term>& t1 = n1.terms;
    std::vector<term> terms;
    size_t i0 = 0, i1 = 0;
    while (i0 < t0.size() && i1 < t1.size()) {
      term t;
      if (t0[i0].eqndx(t1[i1])) {
        t = t0[i0];
        t.ki = kstore(f_.addf(kload(t.ki), kload(t1[i1].ki)));
        i0++;
        i1++;
      } else if (t0[i0].ltndx(t1[i1])) {
        t = t0[i0++];
      } else {
        t = t1[i1++];
      }
      push_back_unless_zero(terms, t);
    }

    while (i0 < t0.size()) {
      push_back_unless_zero(terms, t0[i0++]);
    }

    while (i1 < t1.size()) {
      push_back_unless_zero(terms, t1[i1++]);
    }

    return node(terms);
  }

  // constants_[n] stores the n-th constant, once.
  // Modulo collisions, constants_[constttab_[hash(k)]] == k
  // for k \in Elt.
  std::vector<Elt> constants_;
  PdqHash consttab_;

  std::vector<node> nodes_;
  PdqHash cse_;

  size_t kstore(const Elt& k) {
    uint64_t d = elt_hash(k, f_);
    auto pred = [&](PdqHash::value_t ki) { return k == constants_[ki]; };
    size_t ki = consttab_.find(d, pred);

    if (ki == PdqHash::kNil) {
      ki = constants_.size();
      constants_.push_back(k);
      consttab_.insert(d, ki);
    }
    return ki;
  }
  Elt& kload(size_t ki) { return constants_[ki]; }

  void mark_needed(size_t op, size_t depth_at_which_needed) {
    nodeinfo* nfo = &nodes_[op].info;
    nfo->is_needed = true;
    nfo->max_needed_depth =
        std::max<size_t>(depth_at_which_needed, nfo->max_needed_depth);

    // If DEPTH_AT_WHICH_NEEDED > DEPTH + 1, we need a constant 1 at
    // depth DEPTH_AT_WHICH_NEEDED-1 (and implicily any lower depths) in
    // order to copy the node across levels.
    if (depth_at_which_needed > nfo->depth + 1) {
      nodeinfo* nfo0 = &nodes_[0].info;
      nfo0->is_needed = true;
      nfo0->max_needed_depth =
          std::max<size_t>(depth_at_which_needed - 1, nfo0->max_needed_depth);
    }
  }

  size_t compute_depth_ub() {
    size_t r = 0;
    for (auto& n : nodes_) {
      if (n.info.is_output) {
        r = std::max<size_t>(r, 1 + n.info.depth);
      } else if (n.info.is_assert0) {
        // Assertions of the form 0*(1*OP) contibute n.info.depth and
        // not 1 + n.info.depth.  If the assertion is in the last
        // layer, it will be transformed in an output of OP at
        // n.info.depth.  If the assertion is not in the last layer,
        // then it doesn't matter whether we use DEPTH or 1 + DEPTH.
        if (n.linearp()) {
          r = std::max<size_t>(r, n.info.depth);
        } else {
          r = std::max<size_t>(r, 1 + n.info.depth);
        }
      }
    }
    depth_ = r;
    return r;
  }

  void fixup_last_layer_assertions(size_t depth_ub) {
    // convert assertions in the last layer into outputs
    for (auto& n : nodes_) {
      if (!n.info.is_output && n.info.is_assert0 && n.info.depth == depth_ub &&
          n.linearp()) {
        n.info.is_assert0 = false;
        output_internal(n.terms[0].op1, nodeinfo::kWireIdUndefined);
      }
    }
  }

  void compute_needed(size_t depth_ub) {
    nwires_not_needed_ = 0;
    for (size_t i = nodes_.size(); i-- > 0;) {
      nodeinfo* nfo = &nodes_[i].info;

      // mark all inputs as needed, to prevent ambiguity
      // in the layout of the W[] vector.
      if (nfo->is_input) {
        mark_needed(i, 1);
      }
      // outputs are needed at depth_ub_
      if (nfo->is_output) {
        mark_needed(i, depth_ub);
      }
      // assertions are needed in the next layer
      if (nfo->is_assert0) {
        mark_needed(i, nfo->depth + 1);
      }

      if (nfo->is_needed) {
        for (const auto& t : nodes_[i].terms) {
          mark_needed(t.op0, nfo->depth);
          mark_needed(t.op1, nfo->depth);
        }
      } else {
        ++nwires_not_needed_;
      }
    }
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_COMPILER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_CONSTANTS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_CONSTANTS_H_

#include <stddef.h>
#include <stdint.h>

namespace proofs {

/* Max number of SHA blocks to process. */
constexpr static const size_t kMaxSHABlocks = 35;

/* Number of bits in CBOR index. Must be large enough to index into MDOC.*/
constexpr static const size_t kCborIndexBits = 12;

// This is the prefix added to the D8... mdoc encoding to produce
// a COSE1 encoding that is ready to be hashed.
static constexpr uint8_t kCose1Prefix[18] = {
    0x84, 0x6A, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75,
    0x72, 0x65, 0x31, 0x43, 0xA1, 0x01, 0x26, 0x40, 0x59,
};
static constexpr size_t kCose1PrefixLen = 18;

/* Max size of an MSO that hashes using < MAX SHA blocks. */
constexpr static const size_t kMaxMsoLen =
    kMaxSHABlocks * 64 - 9 - kCose1PrefixLen;

static constexpr size_t kValidityInfoLen = 12;
static constexpr size_t kValidFromLen = 9;
static constexpr size_t kDeviceKeyLen = 9;
static constexpr size_t kDeviceKeyInfoLen = 13;
static constexpr size_t kValidUntilLen = 10;
static constexpr size_t kValueDigestsLen = 12;
static constexpr size_t kOrgLen = 17;

static constexpr uint8_t kTag32[] = {0x58, 0x20};
static constexpr size_t kIdLen = 32;
static constexpr size_t kValueLen = 64;

static constexpr uint8_t kValidityInfoID[kValidityInfoLen] = {
    'v', 'a', 'l', 'i', 'd', 'i', 't', 'y', 'I', 'n', 'f', 'o'};

static constexpr uint8_t kValidFromID[kValidFromLen] = {'v', 'a', 'l', 'i', 'd',
                                                        'F', 'r', 'o', 'm'};

static constexpr uint8_t kValidUntilID[kValidUntilLen] = {
    'v', 'a', 'l', 'i', 'd', 'U', 'n', 't', 'i', 'l'};

static constexpr uint8_t kDeviceKeyID[kDeviceKeyLen] = {'d', 'e', 'v', 'i', 'c',
                                                        'e', 'K', 'e', 'y'};

static constexpr uint8_t kDeviceKeyInfoID[kDeviceKeyInfoLen] = {
    'd', 'e', 'v', 'i', 'c', 'e', 'K', 'e', 'y', 'I', 'n', 'f', 'o'};

static constexpr uint8_t kValueDigestsID[kValueDigestsLen] = {
    'v', 'a', 'l', 'u', 'e', 'D', 'i', 'g', 'e', 's', 't', 's'};

static constexpr uint8_t kOrgID[kOrgLen] = {'o', 'r', 'g', '.', 'i', 's',
                                            'o', '.', '1', '8', '0', '1',
                                            '3', '.', '5', '.', '1'};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_CONSTANTS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_H_

#include <stddef.h>

#include <array>
#include <vector>

#include "algebra/interpolation.h"
#include "algebra/poly.h"
#include "circuits/logic/bit_plucker_constants.h"
#include "circuits/logic/polynomial.h"

namespace proofs {
/*

Many circuits we design require bit inputs in {0,1} when the field F takes 128+
bits to represent. Each input to a circuit is an element in F, and must either
be sent to the verifier or committed, and thus sending several single-bit
inputs represents an overhead.

A bit-plucker is a circuit component that maps a set
S \subset F of size 2^k into k wires, b_0, ..., b_k-1, that are each in {0,1}.
Thus, it can reduce the number of inputs that a predicate circuit requires, and
thus makes the proof smaller or more efficient to compute or verify.

The optimal bit-plucker for k bits depends on k.  For small k, the simplest
bit plucker is sufficient. In some cases, bit pluckers can exploit the field
structure.

[ RUN      ] BitPlucker.PluckSize
pluck[1]: depth:  3 wires: 6 in: 2 out:2 use:4 ovh:2 t:6 cse:0 notn:9
pluck[2]: depth:  4 wires: 14 in: 2 out:4 use:9 ovh:5 t:18 cse:5 notn:19
pluck[3]: depth:  5 wires: 25 in: 2 out:6 use:17 ovh:8 t:38 cse:23 notn:40
pluck[4]: depth:  6 wires: 40 in: 2 out:8 use:29 ovh:11 t:74 cse:73 notn:87
pluck[5]: depth:  7 wires: 61 in: 2 out:10 use:47 ovh:14 t:144 cse:199 notn:194
pluck[6]: depth:  8 wires: 92 in: 2 out:12 use:75 ovh:17 t:288 cse:501 notn:437
pluck[7]: depth:  9 wires: 141 in: 2 out:14 use:121 ovh:20 t:594 cse:1203 notn:984
pluck[8]: depth: 10 wires: 224 in: 2 out:16 use:201 ovh:23 t:1254 cse:2801 notn:2203

Our experiments also considered an O(N)-wires, O(N)-terms bit plucker.
To pluck a LOGN-bit quantity E, write E = N0*E1 + E0 where E0 is a
LOGN0-bit quantity and where E1 is a LOGN1-bit quantity, and where
LOGN0 = ceil(LOGN/2), LOGN1 = floor(LOGN/2).  This decomposition
can be computed by interpolating two Polynomials of length N.  Now
we are left with plucking two quantities E0, E1, which can be done
by any subquadratic-time plucker.

A similar idea for the LOGN -> N binary decoder is in Knuth 7.1.2
Exercise 39.  (A plucker is the moral transpose of the binary
decoder.)

However, this plucker was dominated by the smaller one for our use case, and
thus removed from the code here. It can be resurrected from experimental if
needed.

[ RUN      ] BitPlucker.LargePluckSize
large_pluck[2] depth: 5 wires: 15 in: 2 out:4 use:9 ovh:6 t:19 cse:9 notn:27
large_pluck[3] depth: 7 wires: 31 in: 2 out:5 use:20 ovh:11 t:43 cse:19 notn:50
large_pluck[4] depth: 8 wires: 46 in: 2 out:8 use:33 ovh:13 t:70 cse:33 notn:89
large_pluck[5] depth: 10 wires: 79 in: 2 out:8 use:60 ovh:19 t:128 cse:68 notn:164
large_pluck[6] depth: 11 wires: 119 in: 2 out:12 use:99 ovh:20 t:209 cse:119 notn:299
large_pluck[7] depth: 13 wires: 206 in: 2 out:11 use:179 ovh:27 t:381 cse:234 notn:567
large_pluck[8] depth: 14 wires: 344 in: 2 out:16 use:317 ovh:27 t:668 cse:413 notn:1065
large_pluck[9] depth: 16 wires: 631 in: 2 out:14 use:596 ovh:35 t:1260 cse:796 notn:2064
large_pluck[10] depth: 17 wires: 1157 in: 2 out:20 use:1123 ovh:34 t:2347 cse:1435 notn:3967
large_pluck[11] depth: 19 wires: 2224 in: 2 out:17 use:2181 ovh:43 t:4551 cse:2762 notn:7789
large_pluck[12] depth: 20 wires: 4294 in: 2 out:24 use:4253 ovh:41 t:8782 cse:5113 notn:15205

*/
template <class Logic, size_t LOGN>
class BitPlucker {
 public:
  static constexpr size_t kN = 1 << LOGN;
  static constexpr size_t kNv32Elts = (32u + LOGN - 1u) / LOGN;
  static constexpr size_t kNv256Elts = (256u + LOGN - 1u) / LOGN;
  static constexpr size_t kNv128Elts = (128u + LOGN - 1u) / LOGN;
  using Field = typename Logic::Field;
  using BitW = typename Logic::BitW;
  using EltW = typename Logic::EltW;
  using Elt = typename Field::Elt;
  using PolyN = Poly<kN, Field>;
  using InterpolationN = Interpolation<kN, Field>;
  using v32 = typename Logic::v32;
  using v256 = typename Logic::v256;
  using packed_v32 = std::array<EltW, kNv32Elts>;
  using packed_v128 = std::array<EltW, kNv128Elts>;
  using packed_v256 = std::array<EltW, kNv256Elts>;

  const Logic& l_;
  std::vector<PolyN> plucker_;

  explicit BitPlucker(const Logic& l) : l_(l), plucker_(LOGN) {
    // evaluation points
    PolyN X;
    for (size_t i = 0; i < kN; ++i) {
      X[i] = bit_plucker_point<Field, kN>()(i, l_.f_);
    }
    for (size_t k = 0; k < LOGN; ++k) {
      PolyN Y;
      for (size_t i = 0; i < kN; ++i) {
        Y[i] = l_.f_.of_scalar((i >> k) & 1);
      }
      plucker_[k] = InterpolationN::monomial_of_lagrange(Y, X, l_.f_);
    }
  }

  typename Logic::template bitvec<LOGN> pluck(const EltW& e) const {
    typename Logic::template bitvec<LOGN> r;
    const Logic& L = l_;  // shorthand
    const Polynomial<Logic> P(L);

    for (size_t k = 0; k < LOGN; ++k) {
      EltW v = P.eval(plucker_[k], e);
      L.assert_is_bit(v);
      r[k] = BitW(v, l_.f_);
    }

    return r;
  }

  v32 unpack_v32(const packed_v32& v) const {
    v32 r;
    for (size_t i = 0; i < v.size(); ++i) {
      auto b = pluck(v[i]);
      for (size_t j = 0; j < LOGN; ++j) {
        if (LOGN * i + j < 32) {
          r[LOGN * i + j] = b[j];
        }
      }
    }
    return r;
  }

  template <typename T, typename PackedT>
  T unpack(const PackedT& v) const {
    T r;
    for (size_t i = 0; i < v.size(); ++i) {
      auto b = pluck(v[i]);
      for (size_t j = 0; j < LOGN; ++j) {
        if (LOGN * i + j < r.size()) {
          r[LOGN * i + j] = b[j];
        }
      }
    }
    return r;
  }
};

/*
On input Elt ind, and Elt arr[], returns arr[ind].
This muxer is useful when the same array needs to be muxed multiple times
with different indices.  It differs from the above classes in that it
precomputes the coefficient array, which can depend on EltW inputs.
*/
template <class Logic, size_t LOGN>
class EltMuxer {
  static constexpr size_t kN = 1 << LOGN;

 public:
  using Field = typename Logic::Field;
  using EltW = typename Logic::EltW;
  using PolyN = Poly<kN, Field>;
  using InterpolationN = Interpolation<kN, Field>;

  EltMuxer(const Logic& l, const EltW arr[/* kN */]) : l_(l), coeff_(kN) {
    for (size_t i = 0; i < kN; ++i) {
      coeff_[i] = l_.konst(0);
    }
    for (size_t i = 0; i < kN; ++i) {
      PolyN basis_i = even_lagrange_basis(i);
      for (size_t j = 0; j < kN; ++j) {
        auto bi = l_.konst(basis_i[j]);
        auto barr_i = l_.mul(&bi, arr[i]);
        coeff_[j] = l_.add(&coeff_[j], barr_i);
      }
    }
  }

  EltW mux(const EltW& ind) const {
    const Polynomial<Logic> P(l_);

    std::array<EltW, kN> xi;
    P.powers_of_x(kN, xi.data(), ind);

    // dot product with coefficients
    EltW r = l_.konst(0);
    for (size_t i = 0; i < kN; ++i) {
      auto cxi = l_.mul(&coeff_[i], xi[i]);
      r = l_.add(&r, cxi);
    }
    return r;
  }

 private:
  const Logic& l_;
  std::vector<EltW> coeff_;

  PolyN even_lagrange_basis(size_t k) {
    PolyN X, Y;
    for (size_t i = 0; i < kN; ++i) {
      X[i] = bit_plucker_point<Field, kN>()(i, l_.f_);
      Y[i] = l_.f_.of_scalar((i == k));
    }
    return InterpolationN::monomial_of_lagrange(Y, X, l_.f_);
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_1F_IO_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_1F_IO_H_

#include <cstddef>

#include "circuits/mdoc/mdoc_constants.h"

namespace proofs {

static constexpr size_t kMdoc1DateLen = 20;  // Length of CBOR-formatted time.
static constexpr size_t kMdoc1MaxSHABlocks = 7;
static constexpr size_t kMdoc1CborIndexBits = 9;
static constexpr size_t kMdoc1MaxMsoLen =
    kMdoc1MaxSHABlocks * 64 - 9 - kCose1PrefixLen;
static constexpr size_t kMdoc1SHAPluckerBits = 3;
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_1F_IO_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_PROTO_CIRCUIT_H_
#define PRIVACY_PROOFS_ZK_LIB_PROTO_CIRCUIT_H_

#include <sys/types.h>

#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <optional>
#include <unordered_map>
#include <vector>

#include "algebra/hash.h"
#include "sumcheck/circuit.h"
#include "sumcheck/quad.h"
#include "util/ceildiv.h"
#include "util/panic.h"
#include "util/readbuffer.h"

namespace proofs {

// CircuitRep class handles custom circuit serialization.
//
// We expect circuits to be created and stored locally by the prover and
// verifier respectively. The byte representations are thus assumed to be
// trusted. As a result, the methods below perform only basic sanity checking.
//
// An earlier experiment implemented the IO methods using protobuf parsing.
// Despite applying techniques like arena allocation, those methods required
// several seconds to deserialize the circuit. In contrast, these methods take
// 100s of ms.
//
// This class implements an optimization by which internal indices for
// wire and gate labels and circuit size statistics are stored in a configurable
// number of bytes (kBytesWritten) which we set to 4 instead of 8 to save
// space.  If this value is set to >4, there is a possibility of failure on
// 32b platforms, which currently stops execution.  Thus, all circuits must be
// tested on 32b platforms to ensure they are small enough to work.
enum FieldID {
  NONE = 0,
  P256_ID = 1,
  P384_ID = 2,
  P521_ID = 3,
  GF2_128_ID = 4,
  GF2_16_ID = 5,
  FP128_ID = 6,
  FP64_ID = 7,
  GOLDI_ID = 8,
  FP64_2_ID = 9,
  SECP_ID = 10,
};

template <class Field>
class CircuitRep {
  using Elt = typename Field::Elt;
  using QuadCorner = typename Quad<Field>::quad_corner_t;
  constexpr static size_t kMaxLayers = 10000; /* deep circuits are errors */

 public:
  // Serialize kBytesWritten bytes of a size or index used in the circuit to
  // save space.
  static constexpr size_t kBytesWritten = 3;

  explicit CircuitRep(const Field& f, FieldID field_id)
      : f_(f), field_id_(field_id) {}

  void to_bytes(const Circuit<Field>& sc_c, std::vector<uint8_t>& bytes) {
    EltHash eh(f_);
    bytes.push_back(0x1);  // version
    serialize_field_id(bytes, field_id_);
    serialize_size(bytes, sc_c.nv);
    serialize_size(bytes, sc_c.nc);
    serialize_size(bytes, sc_c.npub_in);
    serialize_size(bytes, sc_c.subfield_boundary);
    serialize_size(bytes, sc_c.ninputs);
    serialize_size(bytes, sc_c.l.size());

    // Scan the circuit to generate the constant table. To keep one
    // scan, write the quad to a separate byte vector and later copy it.
    std::vector<uint8_t> quadb;
    quadb.reserve(1 << 24);
    for (const auto& layer : sc_c.l) {
      serialize_size(quadb, layer.logw);
      serialize_size(quadb, layer.nw);
      serialize_size(quadb, layer.quad->n_);

      QuadCorner prevg(0), prevh0(0), prevh1(0);
      for (size_t i = 0; i < layer.quad->n_; ++i) {
        serialize_index(quadb, layer.quad->c_[i].g, prevg);
        prevg = layer.quad->c_[i].g;
        serialize_index(quadb, layer.quad->c_[i].h[0], prevh0);
        prevh0 = layer.quad->c_[i].h[0];
        serialize_index(quadb, layer.quad->c_[i].h[1], prevh1);
        prevh1 = layer.quad->c_[i].h[1];
        serialize_num(quadb, eh.kstore(layer.quad->c_[i].v));
      }
    }

    serialize_size(bytes, eh.constants_.size());
    for (const auto& v : eh.constants_) {
      uint8_t buf[Field::kBytes];
      f_.to_bytes_field(buf, v);
      bytes.insert(bytes.end(), buf, buf + Field::kBytes);
    }

    bytes.insert(bytes.end(), quadb.begin(), quadb.end());
    bytes.insert(bytes.end(), sc_c.id, sc_c.id + 32);
  }

  // Returns a unique_ptr<Circuit> or nullptr if there is an error in
  // deserializing the circuit.
  std::unique_ptr<Circuit<Field>> from_bytes(ReadBuffer& buf) {
    if (!buf.have(8 * kBytesWritten + 1)) {
      return nullptr;
    }

    uint8_t version = *buf.next(1);
    if (version != 1) {
      return nullptr;
    }

    size_t fid_as_size_t = read_field_id(buf);
    size_t nv = read_size(buf);
    size_t nc = read_size(buf);
    size_t npub_in = read_size(buf);
    size_t subfield_boundary = read_size(buf);
    size_t ninputs = read_size(buf);
    size_t nl = read_size(buf);
    size_t numconst = read_size(buf);

    // Basic sanity checks.
    if (fid_as_size_t != static_cast<size_t>(field_id_) || npub_in > ninputs ||
        subfield_boundary > ninputs || nl > kMaxLayers) {
      return nullptr;
    }

    // Ensure there are enough input bytes for the quad constants.
    auto need = checked_mul(numconst, Field::kBytes);
    if (!need || !buf.have(need.value())) {
      return nullptr;
    }

    std::vector<Elt> constants(numconst);
    for (size_t i = 0; i < numconst; ++i) {
      // Fail if Elt cannot be parsed.
      auto vv = f_.of_bytes_field(buf.next(Field::kBytes));
      if (!vv.has_value()) {
        return nullptr;
      }
      constants[i] = vv.value();
    }

    auto c = std::make_unique<Circuit<Field>>();
    *c = Circuit<Field>{
        .nv = nv,
        .logv = lg(nv),
        .nc = nc,
        .logc = lg(nc),
        .nl = nl,
        .ninputs = ninputs,
        .npub_in = npub_in,
        .subfield_boundary = subfield_boundary,
    };
    c->l.reserve(nl);

    size_t max_g = nv;  // a starting bound on quad number

    for (size_t ly = 0; ly < nl; ++ly) {
      // Ensure there are enough input bytes for the layer, 3 values.
      if (!buf.have(3 * kBytesWritten)) {
        return nullptr;
      }

      size_t lw = read_size(buf);
      size_t nw = read_size(buf);
      size_t nq = read_size(buf);

      // Each quad takes 4 values, check for overflow.
      need = checked_mul(4 * kBytesWritten, nq);
      if (!need || !buf.have(need.value())) {
        return nullptr;
      }

      auto qq = std::make_unique<Quad<Field>>(nq);
      size_t prevg = 0, prevhl = 0, prevhr = 0;
      for (size_t i = 0; i < nq; ++i) {
        size_t g = read_index(buf, prevg);
        if (g > max_g) {  // index of quad must be < wires in the layer
          return nullptr;
        }
        prevg = g;
        size_t hl = read_index(buf, prevhl);
        size_t hr = read_index(buf, prevhr);
        if (hl > nw || hr > nw) {
          return nullptr;
        }
        prevhl = hl;
        prevhr = hr;
        size_t vi = read_num(buf);
        if (vi >= numconst) {
          return nullptr;
        }

        qq->c_[i] = typename Quad<Field>::corner{
            QuadCorner(g), {QuadCorner(hl), QuadCorner(hr)}, constants[vi]};
      }
      c->l.push_back(Layer<Field>{
          .nw = nw,
          .logw = lw,
          .quad = std::unique_ptr<const Quad<Field>>(std::move(qq))});
      max_g = nw;
    }
    // Read the circuit name from the serialization.
    if (!buf.have(32)) {
      return nullptr;
    }
    buf.next(32, c->id);
    return c;
  }

 private:
  static constexpr uint64_t kMaxValue = (1ULL << (kBytesWritten * 8)) - 1;

  // Multiplies arguments and checks for overflow.
  template <typename T>
  std::optional<T> checked_mul(T a, T b) {
    T ab = a * b;
    if (a == 0 || ab / a == b) return ab;
    return std::nullopt;
  }

  static void serialize_field_id(std::vector<uint8_t>& bytes, FieldID id) {
    serialize_num(bytes, static_cast<size_t>(id));
  }

  static void serialize_size(std::vector<uint8_t>& bytes, size_t sz) {
    serialize_num(bytes, sz);
  }

  // We write indices as differences from the previous index.  This
  // encoding appears to produce byte streams that compress better
  // under both gzip and xz compression.  For example, xz compresses
  // a 35MB test circuit to 2MB without delta encoding, and to 100KB
  // with delta encoding.  We have no real theory to explain this
  // phenomenon, but at least part of the reason is that the deltas
  // are usually smaller than the indices.
  //
  static void serialize_index(std::vector<uint8_t>& bytes, QuadCorner ind0,
                              QuadCorner prev_ind0) {
    size_t ind = static_cast<size_t>(ind0);
    size_t prev_ind = static_cast<size_t>(prev_ind0);

    // Encode the delta IND - PREV_IND.  Since the delta can be
    // negative, and the rest of the code is unsigned only,
    // use the LSB as sign bit.
    if (ind >= prev_ind) {
      serialize_num(bytes, 2u * (ind - prev_ind));
    } else {
      serialize_num(bytes, 2u * (prev_ind - ind) + 1u);
    }
  }

  static void serialize_num(std::vector<uint8_t>& bytes, size_t g) {
    check(g < kMaxValue, "Violating small wire-label assumption");
    uint8_t tmp[kBytesWritten];
    for (size_t i = 0; i < kBytesWritten; ++i) {
      tmp[i] = static_cast<uint8_t>(g & 0xff);
      g >>= 8;
    }
    bytes.insert(bytes.end(), tmp, tmp + kBytesWritten);
  }

  // These routine reads bytes written by serialize_* methods, and thus
  // only needs to handle values expressed in kBytesWritten.
  // On 32b platforms, some large circuits may fail; this method
  // causes a failure in that case.

  // Do not cast to FieldID, since the input is untrusted and the
  // cast may fail.
  static size_t read_field_id(ReadBuffer& buf) { return read_num(buf); }

  static size_t read_size(ReadBuffer& buf) { return read_num(buf); }

  static size_t read_index(ReadBuffer& buf, size_t prev_ind) {
    size_t delta = read_num(buf);
    if (delta & 1) {
      return prev_ind - (delta >> 1);
    } else {
      return prev_ind + (delta >> 1);
    }
  }

  static size_t read_num(ReadBuffer& buf) {
    uint64_t r = 0;
    const uint8_t* p = buf.next(kBytesWritten);
    for (size_t i = 0; i < kBytesWritten; ++i) {
      r |= (p[i] << (i * 8));
    }

    // SIZE_MAX is system defined as max value for size_t.
    // This check fails if a large circuit is loaded on a 32b machine.
    check(r < SIZE_MAX, "Violating small wire-label assumption");
    return static_cast<size_t>(r);
  }

  // Class that defines the hash function for Elt.
  class EHash {
   public:
    const Field& f_;
    explicit EHash(const Field& f) : f_(f) {}
    size_t operator()(const Elt& k) const { return elt_hash(k, f_); }
  };

  // This structure encapsulates the hash used by the compiler.
  class EltHash {
   public:
    std::vector<Elt> constants_;

    explicit EltHash(const Field& f) : f_(f), table_(1000, EHash(f)) {}

    size_t kstore(const Elt& k) {
      if (auto search = table_.find(k); search != table_.end()) {
        return search->second;
      }

      size_t ki = constants_.size();
      constants_.push_back(k);
      table_[k] = ki;
      return ki;
    }

   private:
    const Field& f_;
    std::unordered_map<Elt, size_t, EHash> table_;
  };

  const Field& f_;
  FieldID field_id_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_PROTO_CIRCUIT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ZK_ZK_COMMON_H_
#define PRIVACY_PROOFS_ZK_LIB_ZK_ZK_COMMON_H_

#include <cstddef>
#include <vector>

#include "arrays/dense.h"
#include "arrays/eq.h"
#include "arrays/eqs.h"
#include "ligero/ligero_param.h"
#include "random/transcript.h"
#include "sumcheck/circuit.h"
#include "sumcheck/quad.h"
#include "sumcheck/transcript_sumcheck.h"
#include "util/panic.h"

namespace proofs {

template <class Field>
// ZkCommon
//
// Used by prover and verifier to mimic the checks that the sumcheck verifier
// applies to the sumcheck transcript. The difference is that the transcript
// will now be encrypted with a random pad, and the checks will be verified
// by the Ligero proof system with respect to a hiding commitment scheme.
class ZkCommon {
  using index_t = typename Quad<Field>::index_t;
  using Llc = LigeroLinearConstraint<Field>;
  using Elt = typename Field::Elt;
  using CPoly = typename LayerProof<Field>::CPoly;
  using WPoly = typename LayerProof<Field>::WPoly;

 public:
  // pi: witness index for first pad element in a larger commitment
  static size_t verifier_constraints(
      const Circuit<Field>& circuit, const Dense<Field>& pub,
      const Proof<Field>& proof, const ProofAux<Field>* aux,
      std::vector<Llc>& a, std::vector<typename Field::Elt>& b, Transcript& tsv,
      size_t pi, const Field& F) {
    const size_t ninp = circuit.ninputs, npub = circuit.npub_in;

    Challenge<Field> ch(circuit.nl);
    TranscriptSumcheck<Field> tss(tsv, F);

    tss.begin_circuit(ch.q, ch.g);
    Claims cla = Claims{
        .logv = circuit.logv,
        .claim = {F.zero(), F.zero()},
        .q = ch.q,
        .g = {ch.g, ch.g},
    };

    size_t ci = 0;  // Index of the next Ligero constraint.

    const typename WPoly::dot_interpolation dot_wpoly(F);

    // no copies in this version.
    check(circuit.logc == 0, "assuming that copies=1");

    // Constraints from the sumcheck verifier.
    for (size_t ly = 0; ly < circuit.nl; ++ly) {
      auto clr = &circuit.l.at(ly);
      auto plr = &proof.l[ly];
      auto challenge = &ch.l[ly];

      tss.begin_layer(challenge->alpha, challenge->beta, ly);

      // The loop below assumes at least one round.
      check(clr->logw > 0, "clr->logw > 0");

      PadLayout pl(clr->logw);
      ConstraintBuilder cb(pl, F);  // representing 0

      cb.first(challenge->alpha, cla.claim);
      // now cb contains claim_{-1} from the previous layer

      for (size_t round = 0; round < clr->logw; ++round) {
        for (size_t hand = 0; hand < 2; ++hand) {
          size_t r = 2 * round + hand;
          const WPoly& hp = plr->hp[hand][round];
          challenge->hb[hand][round] = tss.round(hp);
          const WPoly lag = dot_wpoly.coef(challenge->hb[hand][round], F);

          cb.next(r, &lag[0], hp.t_);
          // now cb contains a symbolic representation of claim_{r}
        }
      }

      // Verify
      //        claim = EQ[Q,C] QUAD[R,L] W[R,C] W[L,C]
      // by substituting in the symbolic constraint on p(1) from the relation:
      //      claim = <lag, (p(0), p(1), p(2))>.
      Elt quad = aux == nullptr ? bind_quad(clr, cla, challenge, F)
                                : aux->bound_quad[ly];
      Elt eqv =
          Eq<Field>::eval(circuit.logc, circuit.nc, ch.q, challenge->cb, F);
      Elt eqq = F.mulf(eqv, quad);

      // Add the final constraint from above.
      cb.finalize(plr->wc, eqq, ci++, ly, pi, a, b);

      tss.write(&plr->wc[0], 1, 2);

      cla = Claims{
          .logv = clr->logw,
          .claim = {plr->wc[0], plr->wc[1]},
          .q = challenge->cb,
          .g = {challenge->hb[0], challenge->hb[1]},
      };

      pi += pl.layer_size();  // Update index to poly_pad(0,0) of the
                              // next layer
    }

    // Constraints induced by the input binding
    //   <eq0 + alpha.eq1, witness> = W_l + alpha.W_r
    Elt alpha = tsv.elt(F);
    auto plr = &proof.l[circuit.nl - 1];
    Elt got = F.addf(plr->wc[0], F.mulf(alpha, plr->wc[1]));

    return input_constraint(cla, pub, npub, ninp, pi, got, alpha, a, b, ci, F);
  }

  // Returns the size of the proof pad for circuit C.
  static size_t pad_size(const Circuit<Field>& C) {
    size_t sz = 0;
    for (size_t i = 0; i < C.nl; ++i) {
      PadLayout pl(C.l[i].logw);
      sz += pl.layer_size();
    }
    return sz;
  }

  // Setup lqc based on proof pad layout.
  static void setup_lqc(const Circuit<Field>& C,
                        std::vector<LigeroQuadraticConstraint>& lqc,
                        size_t start_pad) {
    size_t pi = start_pad;
    for (size_t i = 0; i < C.nl; ++i) {
      PadLayout pl(C.l[i].logw);
      lqc[i].x = pi + pl.claim_pad(0);
      lqc[i].y = pi + pl.claim_pad(1);
      lqc[i].z = pi + pl.claim_pad(2);
      pi += pl.layer_size();
    }
  }

  // append public parameters to the FS transcript
  static void initialize_sumcheck_fiat_shamir(Transcript& ts,
                                              const Circuit<Field>& circuit,
                                              const Dense<Field>& pub,
                                              const Field& F) {
    ts.write(circuit.id, sizeof(circuit.id));

    // Public inputs:
    for (size_t i = 0; i < circuit.npub_in; ++i) {
      ts.write(pub.at(i), F);
    }

    // Outputs pro-forma:
    ts.write(F.zero(), F);

    // Enough zeroes for correlation intractability, one byte
    // per term.
    ts.write0(circuit.nterms());
  }

 private:
  // The claims struct mimics the same object in the sumcheck code. This
  // helps the verifier_constraints method above mimic the same steps as
  // the sumcheck verifier.
  struct Claims {
    size_t logv;
    Elt claim[2];
    const Elt* q;
    const Elt* g[2];
  };

  class PadLayout {
    size_t logw_;

   public:
    explicit PadLayout(size_t logw) : logw_(logw) {}

    // Layout of padding in the expr_.symbolic array.
    //
    // A *claim pad* is a triple [dWC[0], dWC[1], dWC[0]*dWC[1]].
    //
    // A *poly pad* is a pair [dP(0), dP(2)], where "2" is a generic
    // name for the third evaluation point of the sumcheck round
    // polynomial (could be X for binary fields GF(2)[X] / (Q(X))).
    //
    // The layout of expr_.symbolic is
    //  [CLAIM_PAD[layer - 1], POLY_PAD[0], POLY_PAD[1], ..
    //   POLY_PAD[LOGW - 1], CLAIM_PAD[layer]]
    //
    // The layout of adjacent layers thus overlaps.  For layer 0
    // we still lay out CLAIM_PAD[layer - 1] to keep the representation
    // uniform, but we don't output the corresponding Ligero terms.

    // Because of different use cases, we have two indexing schemes:
    //
    //  "with overlap":     the first element is CLAIM_PAD[layer - 1][0]
    //  "without overlap":  the first element is POLY_PAD[0][0]

    //------------------------------------------------------------
    // Indexing without overlap.
    //------------------------------------------------------------
    size_t poly_pad(size_t r, size_t point) const {
      check(point == 0 || point == 2, "unknown poly_pad() layout");
      if (point == 0) {
        return 2 * r;
      } else if (point == 2) {
        return 2 * r + 1;
      }
      return 0;  // silence noreturn warning
    }
    // index of CLAIM_PAD[layer][n]
    size_t claim_pad(size_t n) const { return poly_pad(2 * logw_, 0) + n; }

    // size of the layer
    size_t layer_size() const { return claim_pad(3); }

    //------------------------------------------------------------
    // Indexing with overlap.
    //------------------------------------------------------------
    // index of CLAIM_PAD[layer - 1][n]
    size_t ovp_claim_pad_m1(size_t n) const { return n; }
    size_t ovp_poly_pad(size_t r, size_t point) const {
      return 3 + poly_pad(r, point);
    }
    size_t ovp_claim_pad(size_t n) const { return 3 + claim_pad(n); }
    size_t ovp_layer_size() const { return ovp_claim_pad(3); }
  };

  // Represent symbolic expressions of the form
  //
  // KNOWN + SUM_{i} SYMBOLIC[i] * WITNESS[i]
  //
  // and support simple linear operations on such quantities
  class Expression {
    Elt known_;
    std::vector<Elt> symbolic_;
    const Field& f_;

   public:
    Expression(size_t nvar, const Field& F)
        : known_(F.zero()), symbolic_(nvar, F.zero()), f_(F) {}

    Elt known() { return known_; }
    std::vector<Elt> symbolic() { return symbolic_; }

    void scale(const Elt& k) {
      f_.mul(known_, k);
      for (auto& e : symbolic_) {
        f_.mul(e, k);
      }
    }

    // We don't need the general case of combining two
    // Expressions.  Instead, we only need the two operations
    // below.

    // *this += k * (known_value + witness[var]).
    void axpy(size_t var, const Elt& known_value, const Elt& k) {
      f_.add(known_, f_.mulf(k, known_value));
      f_.add(symbolic_[var], k);
    }

    // *this -= k * (known_value + witness[var])
    void axmy(size_t var, const Elt& known_value, const Elt& k) {
      f_.sub(known_, f_.mulf(k, known_value));
      f_.sub(symbolic_[var], k);
    }
  };

  class ConstraintBuilder {
    Expression expr_;
    const PadLayout& pl_;
    const Field& f_;

   public:
    ConstraintBuilder(const PadLayout& pl, const Field& F)
        : expr_(pl.ovp_layer_size(), F), pl_(pl), f_(F) {}

    // For given unpadded variable X in the original non-ZK prover,
    // the transcript contains the padded variable Xhat = X - dX
    // where dX is the padding of X.  Thus the unpadded variable is
    //
    //    X = Xhat + dX
    //
    // The ZK verifier needs to compute linear combinations (and one
    // quadratic combination) of the X's, but it only has access to
    // the Xhat's and to a committment to the dX's.  We also want to
    // discuss the verifier algorithm as if the verifier were
    // operating on X, in order to keep the discussion simple.
    //
    // To this end, the Expression class keeps a symbolic
    // representation of a variable X as
    //
    //     X = KNOWN + SUM_{i} SYMBOLIC[i] dX[i]
    //
    // which is sufficient to capture any linear combination of
    // X variables.  We do something special for the quadratic
    // combination in finalize().

    // We store only one quantity EXPR_ that represents either
    // p(1) at some certain round, or a claim at some round.
    // Comments make it clear which is which.

    // Initially, compute claim_{-1} = cl0 + alpha*cl1
    void first(Elt alpha, const Elt claims[]) {
      // expr_ contains zero
      expr_.axpy(pl_.ovp_claim_pad_m1(0), claims[0], f_.one());
      expr_.axpy(pl_.ovp_claim_pad_m1(1), claims[1], alpha);
      // expr_ contains claim_{-1} = cl0 + alpha*cl1
    }

    // Given claim_{r-1}, compute claim_{r}
    void next(size_t r, const Elt lag[], const Elt tr[]) {
      // expr contains claim_{r-1}
      expr_.axmy(pl_.ovp_poly_pad(r, 0), tr[0], f_.one());
      // expr contains p_{r}(1) = claim_{r-1} - p_{r}(0)

      // Compute the dot-product <lag_{r}, p_{r}> in place:
      //  claim_{r} = p_{r}(1) * lag[1], overwriting expr_
      //  claim_{r} += lag[0] * p_{r}(0)
      //  claim_{r} += lag[2] * p_{r}(2)
      expr_.scale(lag[1]);
      expr_.axpy(pl_.ovp_poly_pad(r, 0), tr[0], lag[0]);
      expr_.axpy(pl_.ovp_poly_pad(r, 2), tr[2], lag[2]);
      // expr_ contains claim_{r} = <lag_{r}, p_{r}>
    }

    // The finalize method uses the last sumcheck claim to
    // add a constraint on the dX's (the pad) to the Ligero system.
    //
    // Our goal is to verify that
    //
    //            CLAIM = EQQ * W[R,C] * W[L,C]
    //
    // where EQQ = EQ[Q,C] QUAD[R,L] and all variables are unpadded.
    //
    // We have a symbolic representation of CLAIM in expr_, the proof
    // contains W_hat[{R,L},C], the padding witnesses are at index pi,
    // pi+1, and their product is at index pi+2.
    //
    // Let CLAIM = KNOWN + SUM_{i} SYMBOLIC[i] dX[i] from the
    // Expression class.  Then
    //
    //     KNOWN + SUM_{i} SYMBOLIC[i] dX[i]
    //        = EQQ * (W_hat[R,C] + dW[R,C]) * (W_hat[L,C] + dW[L,C])
    //
    // Rearranging in the Ax = b form needed for ligero, we have
    //
    //  SUM_{i} SYMBOLIC[i] dX[i] - (EQQ * W[R, C]) dW[L, C]
    //      - (EQQ * W[L, C]) dW[R, C] - EQQ * dW[R,C] * dW[L,C]
    //   = EQQ * W[R,C] * W[L,C] - KNOWN
    void finalize(const Elt wc[], const Elt& eqq, size_t ci, size_t ly,
                  size_t pi, std::vector<Llc>& a, std::vector<Elt>& b) {
      // break the Expression abstraction and split into constituents.

      // EQQ * W[R,C] * W[L,C] - known
      Elt rhs = f_.subf(f_.mulf(eqq, f_.mulf(wc[0], wc[1])), expr_.known());

      // symbolic part
      std::vector<Elt> lhs = expr_.symbolic();
      f_.sub(lhs[pl_.ovp_claim_pad(0)], f_.mulf(eqq, wc[1]));
      f_.sub(lhs[pl_.ovp_claim_pad(1)], f_.mulf(eqq, wc[0]));
      f_.sub(lhs[pl_.ovp_claim_pad(2)], eqq);

      b.push_back(rhs);

      // Layer 0 does not refer to CLAIM_PAD[layer - 1]
      size_t i0 = (ly == 0) ? pl_.ovp_poly_pad(0, 0) : pl_.ovp_claim_pad_m1(0);

      for (size_t i = i0; i < lhs.size(); ++i) {
        // "i" is in the "with overlap" reference frame.
        // "pi" is in the "without overlap" reference frame.
        //
        // In theory at least, (pi - pl_.ovp_poly_pad(0, 0))
        // could overflow, but (pi + i) - pl_.ovp_poly_pad(0, 0) cannot.
        a.push_back(Llc{ci, (pi + i) - pl_.ovp_poly_pad(0, 0), lhs[i]});
      }
    }
  };

  // binding(inputs, R) = binding(pub_inputs, R_p) + binding(witness, R_w)
  // This method explicitly computes the public binding, and then adds the
  // constraints that
  //    binding(witness, R_w) = got - binding(pub_inputs, R_p)
  static size_t input_constraint(const Claims& cla, const Dense<Field>& pub,
                                 size_t pub_inputs, size_t num_inputs,
                                 size_t pi, Elt got, Elt alpha,
                                 std::vector<Llc>& a, std::vector<Elt>& b,
                                 size_t ci, const Field& F) {
    Eqs<Field> eq0(cla.logv, num_inputs, cla.g[0], F);
    Eqs<Field> eq1(cla.logv, num_inputs, cla.g[1], F);
    Elt pub_binding = F.zero();
    for (index_t i = 0; i < num_inputs; ++i) {
      Elt b_i = F.addf(eq0.at(i), F.mulf(alpha, eq1.at(i)));
      if (i < pub_inputs) {
        F.add(pub_binding, F.mulf(b_i, pub.at(i)));
      } else {
        // Use (i - pub_inputs) for the index of private inputs.
        a.push_back(Llc{ci, i - pub_inputs, b_i});
      }
    }

    // We view the input constraints as being at fake layer
    // one past the last real layer.  The alternative of
    // considering the input as part of the last real layer
    // yields code that looks even more convoluted.
    PadLayout pl(/*logw=*/0);

    // This paranoid assertion holds unless the circuit has zero
    // layers, which is not guaranteed by this function alone.
    check(pi >= pl.ovp_poly_pad(0, 0), "pi >= pl.ovp_poly_pad(0, 0)");

    size_t claim_pad_m1 = pi - pl.ovp_poly_pad(0, 0);
    a.push_back(Llc{ci, claim_pad_m1 + 0, F.mone()});
    a.push_back(Llc{ci, claim_pad_m1 + 1, F.negf(alpha)});
    b.push_back(F.subf(got, pub_binding));
    return ++ci;
  }

  static Elt bind_quad(const Layer<Field>* clr, const Claims& cla,
                       const LayerChallenge<Field>* chal, const Field& F) {
    return clr->quad->bind_gh_all(
        // G
        cla.logv, cla.g[0], cla.g[1], chal->alpha, chal->beta,
        // H
        clr->logw, chal->hb[0], chal->hb[1],
        // Field
        F);
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ZK_ZK_COMMON_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_WITNESS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_WITNESS_H_

#include <stddef.h>
#include <stdint.h>

#include <array>

#include "circuits/cbor_parser/cbor_constants.h"
#include "circuits/cbor_parser/cbor_pluck.h"
#include "util/panic.h"

namespace proofs {
template <class Field>
class CborWitness {
 public:
  using Elt = typename Field::Elt;
  static constexpr size_t kNCounters = CborConstants::kNCounters;
  static constexpr size_t kIndexBits = CborConstants::kIndexBits;
  using counters = std::array<size_t, kNCounters>;
  using vindex = std::array<Elt, kIndexBits>;

  struct position_witness {
    Elt encoded_sel_header;

    // SLEN output value, used for debugging but not fed to the circuit
    size_t slen_next_debug;
    // counter values, used for debugging but not fed to the circuit
    counters cc_debug;
    size_t isel_debug;
  };

  struct global_witness {
    Elt invprod_decode;
    Elt cc0;  // initial values of counter[0]
    Elt invprod_parse;
  };

  using v8 = std::array<Elt, 8>;

  explicit CborWitness(const Field& F) : f_(F) {}

  // Return an index as an array of Elt, which can be stored into W[]
  vindex index(size_t j) const {
    const Field& F = f_;  // shorthand
    vindex r;
    for (size_t i = 0; i < kIndexBits; ++i) {
      r[i] = F.of_scalar((j >> i) & 1);
    }
    return r;
  }

  void fill_witnesses(size_t n, size_t input_len, const uint8_t bytes[/*n*/],
                      v8 in[/*n*/], position_witness pw[/*n*/],
                      global_witness& gw) const {
    const Field& F = f_;  // shorthand

    // First pass to compute the number of top-level items.  In the
    // second pass, we will use this value to that all counters are 0
    // at the end of the input.
    size_t top_level_items;
    {
      // start with a value of cc[0] guaranteed not to
      // underflow counter 0.
      counters cc{{n + 1}};

      size_t slen = 1;
      for (size_t i = 0; i < n; ++i) {
        bool overflow;
        bool header = (slen == 1);
        cc = counters_next(bytes[i], header,
                           /*have_nextb=*/(i + 1) < n,
                           /*nextb=*/(i + 1) < n ? bytes[i + 1] : 0, cc,
                           &overflow);
        proofs::check(!overflow, "!overflow");
        slen = next_slen(slen, n, bytes, i);
      }

      top_level_items = (n + 1) - cc[0];
    }

    // second pass starting with the correct counter values
    {
      counters cc{{top_level_items}};
      Elt prod_parse = F.one();
      Elt prod_decode = F.one();

      size_t slen = 1;
      for (size_t i = 0; i < n; ++i) {
        bool overflow;
        bool header = (slen == 1);

        // Require all bytes to be 0 except the last N-INPUT_LEN.
        // That is, the input must be aligned towards the end
        // of arrays, and padded with zeroes at the beginning.
        proofs::check(input_len <= n, "input_len <= n");
        if (i + input_len < n) {
          proofs::check(bytes[i] == 0, "bytes[i] == 0");
        }

        // set up input
        for (size_t j = 0; j < 8; ++j) {
          in[i][j] = F.of_scalar((bytes[i] >> j) & 1);
        }

        if (!header) {
          F.mul(prod_decode, F.of_scalar(slen - 1));
        }

        // set up parse witness
        size_t isel = kNCounters;
        for (size_t l = kNCounters; l-- > 0;) {
          if (cc[l] != 0) {
            if (i > 0) {
              F.mul(prod_parse, F.of_scalar(cc[l]));
            }
            isel = l;
            break;
          }
        }

        cc = counters_next(bytes[i], header,
                           /*have_nextb=*/(i + 1) < n,
                           /*nextb=*/(i + 1) < n ? bytes[i + 1] : 0, cc,
                           &overflow);
        proofs::check(!overflow, "!overflow");
        if (i == 0) {
          gw.cc0 = F.of_scalar(cc[0]);
        }
        pw[i].cc_debug = cc;

        // set up decode witness
        size_t slen_next = next_slen(slen, n, bytes, i);
        pw[i].slen_next_debug = slen_next;

        // encode witnesses
        pw[i].encoded_sel_header =
            cbor_plucker_point<Field, kNCounters>()(header, isel, F);
        pw[i].isel_debug = isel;

        // advance slen
        slen = slen_next;
      }

      gw.invprod_decode = F.invertf(prod_decode);
      gw.invprod_parse = F.invertf(prod_parse);
    }
  }

 private:
  static size_t next_slen(size_t slen, size_t n, const uint8_t bytes[/*n*/],
                          size_t i) {
    size_t slenm1 = slen - 1;
    bool header = (slenm1 == 0);
    if (header) {
      if (i + 1 < n) {
        return item_length(bytes[i], true, bytes[i + 1]);
      } else {
        return item_length(bytes[i], false, 0);
      }
    } else {
      return slenm1;
    }
  }

  // TODO [matteof 2023-11-03] Should not panic() here.
  static size_t item_length(uint8_t b, bool valid_nextb, uint8_t nextb) {
    size_t type = (b >> 5) & 0x7u;
    size_t count = b & 0x1Fu;
    bool count0_23 = (count < 24);
    bool count24 = (count == 24);

    switch (type) {
      case 0: /* unsigned */
      case 1: /* negative integer */
      case 4: /* array */
      case 5: /* map */
      case 6: /* tag */
        if (count0_23) {
          return 1;
        } else if (count24) {
          return 2;
        } else {
          check(false, "unwitnessed count (atom)");
          return 0;
        }

      case 2: /* bytes */
      case 3: /* text */
        if (count0_23) {
          return 1 + count;
        } else if (count24) {
          if (valid_nextb) {
            return 2 + nextb;
          } else {
            check(false, "invalid nextb");
            return 0;
          }
        } else {
          check(false, "unwitnessed count (bytes)");
          return 0;
        }

      case 7: /* special */
        check(false, "unwitnessed special");
        return 0;

      default:
        check(false, "can't happen");
        return 0;
    }
  }

  static size_t decode_count(size_t count_in_header, bool have_nextb,
                             uint8_t nextb) {
    if (count_in_header < 24) {
      return count_in_header;
    } else if (count_in_header == 24) {
      if (have_nextb) {
        return nextb;
      } else {
        check(false, "!have_nextb");
      }
    } else {
      check(false, "count > 24");
    }
    return 0xdeadbeef;
  }

  static counters counters_next(uint8_t b, bool header, bool have_nextb,
                                uint8_t nextb, const counters& c,
                                bool* overflow) {
    size_t type = (b >> 5) & 0x7u;
    size_t count_in_header = b & 0x1Fu;
    bool tagp = (type == 6);
    bool arrayp = (type == 4);
    bool mapp = (type == 5);

    counters c1 = c;
    *overflow = false;

    for (size_t l = kNCounters; l-- > 0;) {
      if (c[l] != 0) {
        if (header) {
          c1[l] = c[l] - 1;

          if (tagp) {
            if (l + 1 < kNCounters) {
              c1[l + 1] = 1;
            } else {
              *overflow = true;
            }
          } else if (arrayp) {
            if (l + 1 < kNCounters) {
              c1[l + 1] = decode_count(count_in_header, have_nextb, nextb);
            } else {
              *overflow = true;
            }
          } else if (mapp) {
            if (l + 1 < kNCounters) {
              c1[l + 1] = 2 * decode_count(count_in_header, have_nextb, nextb);
            } else {
              *overflow = true;
            }
          }
        }
        break;
      }
    }

    return c1;
  }

 private:
  const Field& f_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_WITNESS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_SUMCHECK_PROVER_LAYERS_H_
#define PRIVACY_PROOFS_ZK_LIB_SUMCHECK_PROVER_LAYERS_H_

#include <stddef.h>

#include <memory>
#include <vector>

#include "arrays/affine.h"
#include "arrays/dense.h"
#include "arrays/eqs.h"
#include "sumcheck/circuit.h"
#include "sumcheck/quad.h"
#include "sumcheck/transcript_sumcheck.h"
#include "util/panic.h"

namespace proofs {

// A high level idea is partially described in chapter 4.6.7 "Leveraging Data
// Parallelism for Further Speedups" in the book "Proofs, Arguments, and
// Zero-Knowledge" by Justin Thaler.
template <class Field>
class ProverLayers {
  using Elt = typename Field::Elt;

 public:
  using inputs = std::vector<std::unique_ptr<Dense<Field>>>;

  explicit ProverLayers(const Field& f) : f_(f) {}

  // Evaluate CIRCUIT on input wires W0.  This function stores the
  // input wires of each layer L into IN->at(L), and returns the
  // final output.  This asymmetry reflects the fact that for L
  // layers there are L+1 meaningful sets of wires, and that the
  // prover needs IN while the verifier needs the final output.
  std::unique_ptr<Dense<Field>> eval_circuit(inputs* in,
                                             const Circuit<Field>* circ,
                                             std::unique_ptr<Dense<Field>> W0,
                                             const Field& F) {
    if (in == nullptr || circ == nullptr || W0 == nullptr) return nullptr;

    std::unique_ptr<Dense<Field>> finalV;
    size_t nl = circ->nl, nc = circ->nc;
    check(nl >= 1, "nl >= 1");
    check(nc >= 1, "nc >= 1");

    Dense<Field>* W = W0.get();

    in->resize(nl);
    in->at(nl - 1).swap(W0);

    // Allocate memory and evaluate layer on input W and output V
    for (size_t l = nl; l-- > 0;) {
      Dense<Field>* V;
      if (l > 0) {
        // input of layer l-1 = output of layer l
        in->at(l - 1) = std::make_unique<Dense<Field>>(nc, circ->l[l - 1].nw);
        V = in->at(l - 1).get();
      } else {
        // final output = output of layer 0
        finalV = std::make_unique<Dense<Field>>(nc, circ->nv);
        V = finalV.get();
      }

      bool ok = eval_quad(circ->l[l].quad.get(), V, W, F);
      if (!ok) {
        // Early exit in case of assertion failure.
        // In this case IN is only partially allocated.
        // To avoid ambiguities, free all memory that we may have allocated.
        for (size_t i = 0; i < nl; ++i) {
          in->at(i) = nullptr;
        }
        finalV = nullptr;

        return /*finalV=*/nullptr;
      }

      W = V;
    }

    return finalV;
  }

 protected:
  const Field& f_;

  // A struct that collects the bindings generated while proving one
  // layer, to serve as initial bindings for the next layer.
  // This protected class must be defined before the public section.
  struct bindings {
    size_t logv;
    Elt q[Proof<Field>::kMaxBindings];
    Elt g[2][Proof<Field>::kMaxBindings];
  };

  // Generate proof for circuit, as a protected member, the caller must
  // ensure that input parameters are valid.
  void prove(Proof<Field>* pr, const Proof<Field>* pad,
             const Circuit<Field>* circ, const inputs& in, ProofAux<Field>* aux,
             bindings& bnd, TranscriptSumcheck<Field>& ts, const Field& F) {
    size_t logc = circ->logc;
    corner_t nc = circ->nc;

    check(circ->logv <= Proof<Field>::kMaxBindings,
          "CIRCUIT->logv <= kMaxBindings");
    bnd.logv = circ->logv;

    // obtain the initial Q and G[0] bindings from the verifier
    ts.begin_circuit(bnd.q, bnd.g[0]);

    // Duplicate the g[0] binding.
    // In general, the prover step takes two claims G[0], G[1] on the output
    // wires and reduces them to one claim on G[0] + alpha * G[1] for random
    // alpha. However, in the first step, there is only one claim, so we
    // need to make up G[1]. The code sets G[1] = G[0] and it doesn't affect
    // soundness.
    for (size_t i = 0; i < bnd.logv; ++i) {
      bnd.g[1][i] = bnd.g[0][i];
    }

    for (size_t ly = 0; ly < circ->nl; ++ly) {
      auto clr = &circ->l.at(ly);
      Elt alpha, beta;
      ts.begin_layer(alpha, beta, ly);
      Eqs<Field> EQ(logc, nc, bnd.q, F);
      auto QUAD = clr->quad->clone();
      QUAD->bind_g(bnd.logv, bnd.g[0], bnd.g[1], alpha, beta, F);

      layer(pr, pad, ts, bnd, ly, logc, clr->logw, &EQ, QUAD.get(),
            in.at(ly).get(), F);

      if (aux != nullptr) {
        aux->bound_quad[ly] = QUAD->scalar();
      }
    }
  }

 private:
  using index_t = typename Quad<Field>::index_t;
  using CPoly = typename LayerProof<Field>::CPoly;
  using WPoly = typename LayerProof<Field>::WPoly;

  /*
  Engage in single-layer sumcheck on

          EQ[|c] QUAD[|r,l] W[r,c] W[l,c]

  Bind c to C, r to R, and l to L (in that order).  Store claims
  W[R,C] and W[L,C] in the proof, and set BND to the new bindings for
  the next layer.

  logw: number of sumcheck rounds in r, l
  logc: number of sumcheck rounds in c
  */
  void layer(Proof<Field>* pr, const Proof<Field>* pad,
             TranscriptSumcheck<Field>& ts, bindings& bnd, size_t layer,
             size_t logc, size_t logw, Eqs<Field>* EQ, Quad<Field>* QUAD,
             Dense<Field>* W, const Field& F) {
    check(EQ->n() == W->n0_, "EQ->n() == W->n0_");

    check(logw <= Proof<Field>::kMaxBindings, "logw <= kMaxBindings");
    bnd.logv = logw;

    // Bind the C variables to Q.
    // Note that binding C variables takes O(number_of_copies * circuit_size)
    // while binding R, L takes O(circuit_size * log(circuit_size)). In most
    // cases number_of_copies > log(circuit_size), so we don't have to
    // optimize binding R, L.
    for (size_t round = 0; round < logc; ++round) {
      CPoly sum{};

      // sum over r,l: QUAD[|r,l] EQ[|c] W[r,c] W[l,c]
      for (index_t i = 0; i < QUAD->n_; i++) {
        corner_t r(QUAD->c_[i].h[0]);
        corner_t l(QUAD->c_[i].h[1]);

        // sum over c: EQ[|c] W[r,c] W[l,c]
        CPoly sumc{};

        // n0_ is the copy dimension, n1_ is the wire dimension.
        for (corner_t c = 0; c < W->n0_; c += 2) {
          CPoly poly = cpoly_at_dense(EQ, c, 0, F)
                           .mul(cpoly_at_dense(W, c, r, F), F)
                           .mul(cpoly_at_dense(W, c, l, F), F);
          sumc.add(poly, F);
        }

        sumc.mul_scalar(QUAD->c_[i].v, F);
        sum.add(sumc, F);
      }

      Elt rnd = round_c(pr, pad, ts, layer, round, sum, F);
      bnd.q[round] = rnd;

      // bind the c variable in both EQ and W
      EQ->bind(rnd, F);
      W->bind(rnd, F);
    }

    Elt eq0 = EQ->scalar();

    W->reshape(W->n1_);
    check(W->n1_ == 1, "W->n1_ == 1");

    auto Wclone = W->clone();                 // keep alive until function end
    Dense<Field>* WH[2] = {W, Wclone.get()};  // reuse W

    for (size_t round = 0; round < logw; ++round) {
      for (size_t hand = 0; hand < 2; hand++) {
        // In SUM_{l,r} Q[l,r] W[l] W[r], first precompute QW[l] =
        // SUM_{r} Q[l,r] W[r] as a dense array, and then compute
        // SUM_{l} QW[l] W[l].
        Dense<Field> QW(WH[hand]->n0_, 1);
        QW.clear(F);
        size_t ohand = 1 - hand;

        // QW[l] = SUM_{r} Q[l,r] W[r]
        for (index_t i = 0; i < QUAD->n_; ++i) {
          corner_t p0(QUAD->c_[i].h[hand]);
          corner_t p1(QUAD->c_[i].h[ohand]);
          F.add(QW.v_[p0], F.mulf(QUAD->c_[i].v, WH[ohand]->v_[p1]));
        }

        // SUM_{l} QW[l] W[l].
        WPoly sum{};
        for (corner_t l = 0; l < QW.n0_; l += 2) {
          WPoly poly = wpoly_at_dense(WH[hand], l, 0, F)
                           .mul(wpoly_at_dense(&QW, l, 0, F), F);
          sum.add(poly, F);
        }

        sum.mul_scalar(eq0, F);
        Elt rnd = round_h(pr, pad, ts, layer, hand, round, sum, F);
        bnd.g[hand][round] = rnd;

        // bind the r variable in W[hand] and QUAD
        WH[hand]->bind(rnd, F);
        QUAD->bind_h(rnd, hand, F);
      }
    }

    QUAD->scalar();  // for the side effect of assertions
    Elt WC[2] = {WH[0]->scalar(), WH[1]->scalar()};
    end_layer(pr, pad, ts, layer, WC, F);
  }

  // Evaluate the quadratic form
  //
  //         V[g,c] = QUAD[g|r,l] W[r,c] W[l,c]
  //
  // Returns false in the case the quad is an assert0 check that fails.
  bool eval_quad(const Quad<Field>* quad, Dense<Field>* V,
                 const Dense<Field>* W, const Field& F) {
    check(V->n0_ == W->n0_, "V->n0_ == W->n0_");
    corner_t n0 = V->n0_;

    V->clear(F);
    for (index_t i = 0; i < quad->n_; i++) {
      corner_t g(quad->c_[i].g);
      corner_t r(quad->c_[i].h[0]);
      corner_t l(quad->c_[i].h[1]);
      for (corner_t c = 0; c < n0; ++c) {
        auto x = quad->c_[i].v;
        if (x == F.zero()) {
          // assert that the computed W[l]W[r] is zero.
          auto y = W->v_[n0 * l + c];
          F.mul(y, W->v_[n0 * r + c]);
          if (y != F.zero()) {
            return false;
          }
        } else {
          F.mul(x, W->v_[n0 * l + c]);
          F.mul(x, W->v_[n0 * r + c]);
          F.add(V->v_[n0 * g + c], x);
        }
      }
    }
    return true;
  }

  Elt /*R*/ round_c(Proof<Field>* pr, const Proof<Field>* pad,
                    TranscriptSumcheck<Field>& ts, size_t layer, size_t round,
                    CPoly poly, const Field& F) {
    check(round <= Proof<Field>::kMaxBindings, "round <= kMaxBindings");

    if (pad) {
      poly.sub(pad->l[layer].cp[round], F);
    }

    pr->l[layer].cp[round] = poly;
    return ts.round(poly);
  }

  Elt /*R*/ round_h(Proof<Field>* pr, const Proof<Field>* pad,
                    TranscriptSumcheck<Field>& ts, size_t layer, size_t hand,
                    size_t round, WPoly poly, const Field& F) {
    check(round <= Proof<Field>::kMaxBindings, "round <= kMaxBindings");
    if (pad) {
      poly.sub(pad->l[layer].hp[hand][round], F);
    }
    pr->l[layer].hp[hand][round] = poly;
    return ts.round(poly);
  }

  void end_layer(Proof<Field>* pr, const Proof<Field>* pad,
                 TranscriptSumcheck<Field>& ts, size_t layer, const Elt wc[2],
                 const Field& F) {
    Elt tt[2] = {wc[0], wc[1]};
    if (pad) {
      F.sub(tt[0], pad->l[layer].wc[0]);
      F.sub(tt[1], pad->l[layer].wc[1]);
    }

    pr->l[layer].wc[0] = tt[0];
    pr->l[layer].wc[1] = tt[1];

    ts.write(tt, 1, 2);
  }

  CPoly cpoly_at_dense(const Dense<Field>* D, corner_t p0, corner_t p1,
                       const Field& F) {
    return CPoly::extend(D->t2_at_corners(p0, p1, F), F);
  }

  WPoly wpoly_at_dense(const Dense<Field>* D, corner_t p0, corner_t p1,
                       const Field& F) {
    return WPoly::extend(D->t2_at_corners(p0, p1, F), F);
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_SUMCHECK_PROVER_LAYERS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ZK_ZK_PROVER_H_
#define PRIVACY_PROOFS_ZK_LIB_ZK_ZK_PROVER_H_

#include <stddef.h>

#include <memory>
#include <vector>

#include "arrays/dense.h"
#include "ligero/ligero_param.h"
#include "ligero/ligero_prover.h"
#include "random/random.h"
#include "random/transcript.h"
#include "sumcheck/circuit.h"
#include "sumcheck/prover_layers.h"
#include "sumcheck/transcript_sumcheck.h"
#include "util/log.h"
#include "util/panic.h"
#include "zk/zk_common.h"
#include "zk/zk_proof.h"

namespace proofs {
// ZK Prover
//
// This class implements a zero-knowledge argument over a sumcheck transcript
// by first committing to a sumcheck witness and a random pad to encrypt
// a sumcheck transcript, then running the sumcheck protocol over the original
// claim and witness, but outputting the encrypted transcript, and finally
// using a Ligero prover to prove the statement: "the committed witness and
// pad, when used to decrypt the encrypted sumcheck transcript satisfies the
// sumcheck verifier."
//
// While this statement is complex, it can be implemented easily because
// the sumcheck verifier essentially checks the evaluations of degree-2 or -3
// polynomials, and performs one multiplication per layer of the circuit. The
// Hyrax paper makes a similar observation, but uses an elliptic-curve based
// proof, whereas here we use the Ligero system.
template <class Field, class ReedSolomonFactory>
class ZkProver : public ProverLayers<Field> {
  using super = ProverLayers<Field>;
  using typename super::bindings;
  using Elt = typename Field::Elt;
  using typename super::inputs;

 public:
  ZkProver(const Circuit<Field>& CIRCUIT, const Field& F,
           const ReedSolomonFactory& rs_factory)
      : ProverLayers<Field>(F),
        c_(CIRCUIT),
        n_witness_(c_.ninputs - c_.npub_in),
        f_(F),
        rsf_(rs_factory),
        pad_(c_.nl),
        witness_(n_witness_),
        lqc_(c_.nl),
        lp_(nullptr) {}

  void commit(ZkProof<Field>& zkp, const Dense<Field>& W, Transcript& tp,
              RandomEngine& rng) {
    log(INFO, "ZK Commit start");

    // Copy witnesses for commitment
    // Layout of the com: 0 ...<witnesses>... start_pad <pad> len
    // Only commit the private witnesses, which begin at index c_.npub_in.
    for (size_t i = 0; i < n_witness_; ++i) {
      witness_[i] = W.v_[i + c_.npub_in];
    }

    // Rebase the circuit SUBFIELD_BOUNDARY (if any) to start at
    // NPUB_IN,
    size_t subfield_boundary = 0;
    if (c_.subfield_boundary >= c_.npub_in) {
      subfield_boundary = c_.subfield_boundary - c_.npub_in;
    }

    // Fill pad with random values, add pad to witness, record lqc.
    fill_pad(rng);
    ZkCommon<Field>::setup_lqc(c_, lqc_, n_witness_ /* = start_pad */);

    // Commit to witness and pad.
    lp_ = std::make_unique<LigeroProver<Field, ReedSolomonFactory>>(zkp.param);
    lp_->commit(zkp.com, tp, &witness_[0], subfield_boundary, &lqc_[0], rsf_,
                rng, f_);

    log(INFO, "ZK Commitment done");
  }

  bool prove(ZkProof<Field>& zkp, const Dense<Field>& W, Transcript& tsp) {
    check(lp_ != nullptr, "must run commit before prove");

    // Interpret W as public parameters, we only append
    // c_.npub_in elements of W to the transcript
    ZkCommon<Field>::initialize_sumcheck_fiat_shamir(tsp, c_, W, f_);
    Transcript tst = tsp.clone();

    // Run sumcheck to generate a padded proof.
    inputs in;
    auto V = super::eval_circuit(&in, &c_, W.clone(), f_);
    if (V == nullptr) {
      log(ERROR, "eval_circuit failed");
      return false;
    }
    for (size_t i = 0; i < V->n1_; ++i) {
      if (V->v_[i] != f_.zero()) {
        log(ERROR, "V->v_[i] != F.zero()");
        return false;
      };
    }
    bindings bnd;
    ProofAux<Field> aux(c_.nl);

    TranscriptSumcheck<Field> tsts(tst, f_);
    super::prove(&zkp.proof, &pad_, &c_, in, &aux, bnd, tsts, f_);
    log(INFO, "ZK sumcheck done");

    // 5. Simulate the verifier to assemble constraints on the committed vals.
    //    Form the sparse matrix A and vector b such that A*w = b.
    std::vector<LigeroLinearConstraint<Field>> a;
    std::vector<Elt> b;
    size_t ci = ZkCommon<Field>::verifier_constraints(c_, W, zkp.proof, &aux, a,
                                                      b, tsp, n_witness_, f_);
    log(INFO, "ZK constraints done");

    // 6. Produce proof over commitment.
    // For FS soundness, it is ok for hash_of_A to be any string.
    // In the interactive version, the verifier provides a challenge for the
    // com proof. The last prover message is the (wc_l,wc_r) pair, and this
    // has already been added to the transcript.
    const LigeroHash hash_of_A{0xde, 0xad, 0xbe, 0xef};
    lp_->prove(zkp.com_proof, tsp, ci, a.size(), &a[0], hash_of_A, &lqc_[0],
               rsf_, f_);

    log(INFO, "Prover Done: flag");
    return true;
  }

  // Fill proof with random pad values for a given circuit.
  void fill_pad(RandomEngine& rng) {
    for (size_t i = 0; i < c_.nl; ++i) {
      for (size_t j = 0; j < c_.logc; ++j) {
        for (size_t k = 0; k < 4; ++k) {
          if (k != 1) {  // P(1) optimization
            Elt r = rng.elt(f_);
            pad_.l[i].cp[j].t_[k] = r;
            witness_.push_back(r);
          } else {
            pad_.l[i].cp[j].t_[k] = f_.zero();
          }
        }
      }
      for (size_t j = 0; j < c_.l[i].logw; ++j) {
        for (size_t h = 0; h < 2; ++h) {
          for (size_t k = 0; k < 3; ++k) {
            if (k != 1) {  // P(1) optimization
              Elt r = rng.elt(f_);
              pad_.l[i].hp[h][j].t_[k] = r;
              witness_.push_back(r);
            } else {
              pad_.l[i].hp[h][j].t_[k] = f_.zero();
            }
          }
        }
      }
      for (size_t k = 0; k < 2; ++k) {
        Elt r = rng.elt(f_);
        pad_.l[i].wc[k] = r;
        witness_.push_back(r);
      }

      // Commit to product of pads for product proof.
      Elt rr = f_.mulf(pad_.l[i].wc[0], pad_.l[i].wc[1]);
      witness_.push_back(rr);
    }
  }

  const Circuit<Field>& c_;
  const size_t n_witness_;
  const Field& f_;
  const ReedSolomonFactory& rsf_;
  Proof<Field> pad_;
  std::vector<Elt> witness_;
  std::vector<LigeroQuadraticConstraint> lqc_;
  std::unique_ptr<LigeroProver<Field, ReedSolomonFactory>> lp_;
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ZK_ZK_PROVER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ZK_ZK_PROOF_H_
#define PRIVACY_PROOFS_ZK_LIB_ZK_ZK_PROOF_H_

#include <cstddef>
#include <cstdint>
#include <optional>
#include <vector>

#include "ligero/ligero_param.h"
#include "merkle/merkle_commitment.h"
#include "merkle/merkle_tree.h"
#include "sumcheck/circuit.h"
#include "util/log.h"
#include "util/readbuffer.h"
#include "util/serialization.h"
#include "zk/zk_common.h"

namespace proofs {

// ZkProof class handles proof serialization.
//
// We expect circuits to be created and stored locally by the prover and
// verifier respectively, and thus the circuit representations are trusted and
// are assumed to contain parameters that do not induce arithmetic overflows.
// For example, we assume that values like c.logw and c.logc are smaller than
// 2^24 and therefore do not cause any overflows (even on 32b machines) in the
// range/length calculations that are performed during serialization.
//
// An earlier experiment implemented the IO methods using protobuf parsing.
// Despite applying techniques like arena allocation, those methods required
// an order of magnitude more time.
template <class Field>
struct ZkProof {
 public:
  const Circuit<Field> &c;
  Proof<Field> proof;
  LigeroParam<Field> param;
  LigeroCommitment<Field> com;
  LigeroProof<Field> com_proof;

  // The max run length is 2^25, in order to prevent overflow issues on 32b
  // machines when performing length calculations during serialization.
  constexpr static size_t kMaxRunLen = (1 << 25);

  constexpr static size_t kMaxNumDigests = (1 << 25);

  typedef typename Field::Elt Elt;

  explicit ZkProof(const Circuit<Field> &c, size_t rate, size_t req)
      : c(c),
        proof(c.nl),
        param((c.ninputs - c.npub_in) + ZkCommon<Field>::pad_size(c), c.nl,
              rate, req),
        com_proof(&param) {}

  // Maximum size of the proof in bytes. The actual size will be smaller
  // because the Merkle proof is batched.
  size_t size() const {
    return Digest::kLength +

           proof.size() * Field::kBytes +

           com_proof.block * 2 * Field::kBytes +
           com_proof.nreq * com_proof.nrow * Field::kBytes +
           com_proof.nreq * com_proof.mc_pathlen * Digest::kLength;
  }

  void write(std::vector<uint8_t> &buf, const Field &F) const {
    size_t s0 = buf.size();
    write_com(com, buf, F);
    size_t s1 = buf.size();
    write_sc_proof(proof, buf, F);
    size_t s2 = buf.size();
    write_com_proof(com_proof, buf, F);
    size_t s3 = buf.size();
    log(INFO,
        "com:%zu, sc:%zu, com_proof:%zu [%zu el, %zu el, %zu d in %zu "
        "rows]: %zub",
        s1 - s0, s2 - s1, s3 - s2, 2 * com_proof.block,
        com_proof.nreq * com_proof.nrow, com_proof.merkle.path.size(),
        com_proof.nrow, s3);
  }

  // The read function returns false on error or underflow.
  bool read(ReadBuffer &buf, const Field &F) {
    if (!read_com(com, buf, F)) return false;
    if (!read_sc_proof(proof, buf, F)) return false;
    if (!read_com_proof(com_proof, buf, F)) return false;
    return true;
  }

  void write_sc_proof(const Proof<Field> &pr, std::vector<uint8_t> &buf,
                      const Field &F) const {
    check(c.logc == 0, "cannot write sc proof with logc != 0");
    for (size_t i = 0; i < pr.l.size(); ++i) {
      for (size_t wi = 0; wi < c.l[i].logw; ++wi) {
        for (size_t k = 0; k < 3; ++k) {
          // Optimization: do not send p(1) as it is implied by constraints.
          if (k != 1) {
            write_elt(pr.l[i].hp[0][wi].t_[k], buf, F);
            write_elt(pr.l[i].hp[1][wi].t_[k], buf, F);
          }
        }
      }
      write_elt(pr.l[i].wc[0], buf, F);
      write_elt(pr.l[i].wc[1], buf, F);
    }
  }

  void write_com(const LigeroCommitment<Field> &com0, std::vector<uint8_t> &buf,
                 const Field &F) const {
    buf.insert(buf.end(), com0.root.data, com0.root.data + Digest::kLength);
  }

  void write_com_proof(const LigeroProof<Field> &pr, std::vector<uint8_t> &buf,
                       const Field &F) const {
    for (size_t i = 0; i < pr.block; ++i) {
      write_elt(pr.y_ldt[i], buf, F);
    }
    for (size_t i = 0; i < pr.dblock; ++i) {
      write_elt(pr.y_dot[i], buf, F);
    }
    for (size_t i = 0; i < pr.r; ++i) {
      write_elt(pr.y_quad_0[i], buf, F);
    }
    for (size_t i = 0; i < pr.dblock - pr.block; ++i) {
      write_elt(pr.y_quad_2[i], buf, F);
    }

    // write all the Merkle nonces
    for (size_t i = 0; i < pr.nreq; ++i) {
      write_nonce(pr.merkle.nonce[i], buf);
    }

    // The format of the opened rows consists of a run of full-field elements,
    // then a run of base-field elements, and finally a run of full-field
    // elements.  To compress, we employ a run-length encoding approach.
    size_t ci = 0;
    bool subfield_run = false;
    while (ci < pr.nreq * pr.nrow) {
      size_t runlen = 0;
      while (ci + runlen < pr.nreq * pr.nrow && runlen < kMaxRunLen &&
             F.in_subfield(pr.req[ci + runlen]) == subfield_run) {
        ++runlen;
      }
      write_size(runlen, buf);
      for (size_t i = ci; i < ci + runlen; ++i) {
        if (subfield_run) {
          write_subfield_elt(pr.req[i], buf, F);
        } else {
          write_elt(pr.req[i], buf, F);
        }
      }
      ci += runlen;
      subfield_run = !subfield_run;
    }

    write_size(pr.merkle.path.size(), buf);
    for (size_t i = 0; i < pr.merkle.path.size(); ++i) {
      write_digest(pr.merkle.path[i], buf);
    }
  }

 private:
  void write_elt(const Elt &x, std::vector<uint8_t> &buf,
                 const Field &F) const {
    uint8_t tmp[Field::kBytes];
    F.to_bytes_field(tmp, x);
    buf.insert(buf.end(), tmp, tmp + Field::kBytes);
  }

  void write_subfield_elt(const Elt &x, std::vector<uint8_t> &buf,
                          const Field &F) const {
    uint8_t tmp[Field::kSubFieldBytes];
    F.to_bytes_subfield(tmp, x);
    buf.insert(buf.end(), tmp, tmp + Field::kSubFieldBytes);
  }

  void write_digest(const Digest &x, std::vector<uint8_t> &buf) const {
    buf.insert(buf.end(), x.data, x.data + Digest::kLength);
  }

  void write_nonce(const MerkleNonce &x, std::vector<uint8_t> &buf) const {
    buf.insert(buf.end(), x.bytes, x.bytes + MerkleNonce::kLength);
  }

  // Assumption is that all of the sizes of arrays that are part of proofs
  // fit into 4 bytes, and can thus work on 32-b machines.
  void write_size(size_t g, std::vector<uint8_t> &buf) const {
    for (size_t i = 0; i < 4; ++i) {
      buf.push_back(static_cast<uint8_t>(g & 0xff));
      g >>= 8;
    }
  }

  bool read_sc_proof(Proof<Field> &pr, ReadBuffer &buf, const Field &F) {
    if (c.logc != 0) return false;
    for (size_t i = 0; i < pr.l.size(); ++i) {
      size_t needed = (c.l[i].logw * (3 - 1) * 2 + 2) * Field::kBytes;
      if (!buf.have(needed)) return false;
      for (size_t wi = 0; wi < c.l[i].logw; ++wi) {
        for (size_t k = 0; k < 3; ++k) {
          // Optimization: the p(1) value was not sent.
          if (k != 1) {
            for (size_t hi = 0; hi < 2; ++hi) {
              auto v = read_elt(buf, F);
              if (v) {
                pr.l[i].hp[hi][wi].t_[k] = v.value();
              } else {
                return false;
              }
            }
          } else {
            pr.l[i].hp[0][wi].t_[k] = F.zero();
            pr.l[i].hp[1][wi].t_[k] = F.zero();
          }
        }
      }
      for (size_t wi = 0; wi < 2; ++wi) {
        auto v = read_elt(buf, F);
        if (v) {
          pr.l[i].wc[wi] = v.value();
        } else {
          return false;
        }
      }
    }
    return true;
  }

  bool read_com(LigeroCommitment<Field> &com0, ReadBuffer &buf,
                const Field &F) {
    if (!buf.have(Digest::kLength)) return false;
    read_digest(buf, com0.root);
    return true;
  }

  bool read_com_proof(LigeroProof<Field> &pr, ReadBuffer &buf, const Field &F) {
    if (!buf.have(pr.block * Field::kBytes)) return false;
    for (size_t i = 0; i < pr.block; ++i) {
      auto v = read_elt(buf, F);
      if (v) {
        pr.y_ldt[i] = v.value();
      } else {
        return false;
      }
    }

    if (!buf.have(pr.dblock * Field::kBytes)) return false;
    for (size_t i = 0; i < pr.dblock; ++i) {
      auto v = read_elt(buf, F);
      if (v) {
        pr.y_dot[i] = v.value();
      } else {
        return false;
      }
    }

    if (!buf.have(pr.r * Field::kBytes)) return false;
    for (size_t i = 0; i < pr.r; ++i) {
      auto v = read_elt(buf, F);
      if (v) {
        pr.y_quad_0[i] = v.value();
      } else {
        return false;
      }
    }

    if (!buf.have((pr.dblock - pr.block) * Field::kBytes)) return false;
    for (size_t i = 0; i < pr.dblock - pr.block; ++i) {
      auto v = read_elt(buf, F);
      if (v) {
        pr.y_quad_2[i] = v.value();
      } else {
        return false;
      }
    }

    if (!buf.have(pr.nreq * MerkleNonce::kLength)) return false;
    for (size_t i = 0; i < pr.nreq; ++i) {
      read_nonce(buf, pr.merkle.nonce[i]);
    }

    // Decode runs of real and full Field elements.
    size_t ci = 0;
    bool subfield_run = false;
    while (ci < pr.nreq * pr.nrow) {
      if (!buf.have(4)) return false;
      size_t runlen = read_size(buf); /* untrusted size input */
      if (runlen >= kMaxRunLen || ci + runlen > pr.nreq * pr.nrow) return false;
      if (subfield_run) {
        if (!buf.have(runlen * Field::kSubFieldBytes)) return false;
        for (size_t i = ci; i < ci + runlen; ++i) {
          auto v = read_subfield_elt(buf, F);
          if (v) {
            pr.req[i] = v.value();
          } else {
            return false;
          }
        }
      } else {
        if (!buf.have(runlen * Field::kBytes)) return false;
        for (size_t i = ci; i < ci + runlen; ++i) {
          auto v = read_elt(buf, F);
          if (v) {
            pr.req[i] = v.value();
          } else {
            return false;
          }
        }
      }
      ci += runlen;
      subfield_run = !subfield_run;
    }

    if (!buf.have(4)) return false;
    size_t sz = read_size(buf); /* untrusted size input */

    // Merkle proofs of length < NREQ are not valid in the zk proof setting.
    if (sz < pr.nreq || sz >= kMaxNumDigests) return false;  // avoid overflow
    if (!buf.have(sz * Digest::kLength)) return false;

    // Sanity check, the proof should never be larger than this.
    // That value should always fit into memory, so this check aims to avoid
    // an exception by resize() if there is not enough memory to resize.
    if (sz > pr.nreq * pr.mc_pathlen) return false;

    pr.merkle.path.resize(sz);
    for (size_t i = 0; i < sz; ++i) {
      read_digest(buf, pr.merkle.path[i]);
    }
    return true;
  }

  std::optional<Elt> read_elt(ReadBuffer &buf, const Field &F) const {
    return F.of_bytes_field(buf.next(Field::kBytes));
  }

  std::optional<Elt> read_subfield_elt(ReadBuffer &buf, const Field &F) const {
    return F.of_bytes_subfield(buf.next(Field::kSubFieldBytes));
  }

  void read_digest(ReadBuffer &buf, Digest &x) const {
    buf.next(Digest::kLength, x.data);
  }

  void read_nonce(ReadBuffer &buf, MerkleNonce &x) const {
    buf.next(MerkleNonce::kLength, x.bytes);
  }

  size_t read_size(ReadBuffer &buf) { return u32_of_le(buf.next(4)); }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ZK_ZK_PROOF_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_BASE64_DECODE_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_BASE64_DECODE_H_

#include <cstddef>
#include <vector>

#include "util/ceildiv.h"
#include "util/panic.h"

namespace proofs {

// This class implements a circuit to assert a base64 url decoding.
// A string in base64 consists of the characters A-Z a-z 0-9 - _ and =.
// 0--25 are mapped to A-Z, 26--51 are mapped to a-z, 52--61 are mapped to 0-9,
// and 62--63 are mapped to - and _ respectively.
// The base64 encoding is padded with = to a multiple of 4.
template <class LogicCircuit>
class Base64Decoder {
  using EltW = typename LogicCircuit::EltW;
  using BitW = typename LogicCircuit::BitW;
  using v8 = typename LogicCircuit::v8;
  using v6 = typename LogicCircuit::template bitvec<6>;

 public:
  explicit Base64Decoder(const LogicCircuit& lc) : lc_(lc) {}

  void base64_rawurl_decode(const v8 inputs[/*n*/],
                            v8 output[/* ceil(n*6/8) */], size_t n) const {
    check(n < (1 << 28), "input too large");  // avoid overflows
    v6 zero = lc_.template vbit<6>(0);

    size_t max = ceildiv<size_t>(n * 6, 8);
    size_t oc = 0;

    for (size_t i = 0; i < n; i += 4, oc += 3) {
      v6 quad[4] = {zero, zero, zero, zero};
      for (size_t j = 0; j < 4 && i + j < n; ++j) {
        decode(inputs[i + j], quad[j]);
      }
      // repack
      for (size_t j = 0; j < 24 && (oc + j / 8) < max; ++j) {
        output[oc + j / 8][7 - (j % 8)] = quad[j / 6][5 - (j % 6)];
      }
    }
  }

  template <size_t N>
  void base64_rawurl_decode_len(
      const v8 inputs[/*n*/], v8 output[/* ceil(n*6/8) */], size_t n,
      typename LogicCircuit::template bitvec<N>& len) const {
    check(n < (1 << 28), "input too large");  // avoid overflows
    v6 zero = lc_.template vbit<6>(0);

    size_t max = ceildiv<size_t>(n * 6, 8);
    size_t oc = 0;

    for (size_t i = 0; i < n; i += 4, oc += 3) {
      v6 quad[4] = {zero, zero, zero, zero};
      BitW invalid;
      for (size_t j = 0; j < 4 && i + j < n; ++j) {
        decode(inputs[i + j], quad[j], invalid);
        auto range = lc_.vlt(i + j, len);
        lc_.assert_implies(&range, lc_.lnot(invalid));
      }
      // repack
      for (size_t j = 0; j < 24 && (oc + j / 8) < max; ++j) {
        output[oc + j / 8][7 - (j % 8)] = quad[j / 6][5 - (j % 6)];
      }
    }
  }

  void decode(const v8 in, v6& out) const {
    BitW invalid;
    decode(in, out, invalid);
    lc_.assert0(invalid);
  }

  void decode(const v8 in, v6& out, BitW& invalid) const {
    v8 ni;
    for (size_t i = 0; i < 8; ++i) {
      ni[i] = lc_.lnot(in[i]);
    }
    std::vector<std::vector<BitW> > exp[] = {
        {
            //  ['!v4', '!v3', '!v2', '!v1', '!v0']
            {
                ni[4],
                ni[3],
                ni[2],
                ni[1],
                ni[0],
            },
            //  ['v4', 'v3', '!v2', 'v1', 'v0']
            {
                in[4],
                in[3],
                ni[2],
                in[1],
                in[0],
            },
            //  ['v5', 'v4', 'v3', 'v1', 'v0']
            {
                in[5],
                in[4],
                in[3],
                in[1],
                in[0],
            },
            //  ['!v6', 'v3', 'v2', '!v0']
            {
                ni[6],
                in[3],
                in[2],
                ni[0],
            },
            //  ['v4', 'v3', 'v2', '!v1']
            {
                in[4],
                in[3],
                in[2],
                ni[1],
            },
            //  ['v4', 'v3', 'v2', '!v0']
            {
                in[4],
                in[3],
                in[2],
                ni[0],
            },
            //  ['!v6', '!v4', '!v3']
            {
                ni[6],
                ni[4],
                ni[3],
            },
            //  ['!v6', '!v4', '!v2']
            {
                ni[6],
                ni[4],
                ni[2],
            },
            //  ['!v6', 'v3', 'v1']
            {
                ni[6],
                in[3],
                in[1],
            },
            //  ['!v6', '!v5']
            {
                ni[6],
                ni[5],
            },
            //  ['v7']
            {
                in[7],
            },
        },
        {
            //  ['v6', 'v5', 'v4', '!v3', '!v2']
            {
                in[6],
                in[5],
                in[4],
                ni[3],
                ni[2],
            },
            //  ['v6', 'v5', 'v4', '!v3', '!v0']
            {
                in[6],
                in[5],
                in[4],
                ni[3],
                ni[0],
            },
            //  ['v6', 'v5', 'v4', 'v2', '!v1']
            {
                in[6],
                in[5],
                in[4],
                in[2],
                ni[1],
            },
            //  ['v5', 'v2', 'v1', 'v0']
            {
                in[5],
                in[2],
                in[1],
                in[0],
            },
            //  ['v4', 'v3', 'v1', 'v0']
            {
                in[4],
                in[3],
                in[1],
                in[0],
            },
            //  ['v5', 'v3']
            {
                in[5],
                in[3],
            },
            //  ['!v6', '!v2']
            {
                ni[6],
                ni[2],
            },
            //  ['!v6', 'v2']
            {
                ni[6],
                in[2],
            },
        },
        {
            //  ['v5', '!v4', '!v3', '!v1']
            {
                in[5],
                ni[4],
                ni[3],
                ni[1],
            },
            //  ['v5', '!v4', '!v3', '!v2']
            {
                in[5],
                ni[4],
                ni[3],
                ni[2],
            },
            //  ['!v5', 'v4', 'v1']
            {
                ni[5],
                in[4],
                in[1],
            },
            //  ['v5', '!v4', '!v3', '!v0']
            {
                in[5],
                ni[4],
                ni[3],
                ni[0],
            },
            //  ['v4', 'v2', 'v1', 'v0']
            {
                in[4],
                in[2],
                in[1],
                in[0],
            },
            //  ['!v5', 'v4', 'v0']
            {
                ni[5],
                in[4],
                in[0],
            },
            //  ['!v5', 'v4', 'v2']
            {
                ni[5],
                in[4],
                in[2],
            },
            //  ['v4', 'v3']
            {
                in[4],
                in[3],
            },
            //  ['!v6', '!v2']
            {
                ni[6],
                ni[2],
            },
            //  ['!v6', 'v2']
            {
                ni[6],
                in[2],
            },
        },
        {
            //  ['v6', '!v3', '!v2', '!v1', '!v0']
            {
                in[6],
                ni[3],
                ni[2],
                ni[1],
                ni[0],
            },
            //  ['v6', 'v5', 'v4', '!v3', '!v2']
            {
                in[6],
                in[5],
                in[4],
                ni[3],
                ni[2],
            },
            //  ['v6', 'v5', 'v4', '!v3', '!v0']
            {
                in[6],
                in[5],
                in[4],
                ni[3],
                ni[0],
            },
            //  ['v6', 'v5', 'v4', 'v2', '!v1']
            {
                in[6],
                in[5],
                in[4],
                in[2],
                ni[1],
            },
            //  ['v5', '!v4', '!v3', '!v1']
            {
                in[5],
                ni[4],
                ni[3],
                ni[1],
            },
            //  ['v5', '!v4', '!v3', '!v2']
            {
                in[5],
                ni[4],
                ni[3],
                ni[2],
            },
            //  ['v5', '!v4', '!v3', '!v0']
            {
                in[5],
                ni[4],
                ni[3],
                ni[0],
            },
            //  ['!v5', 'v3', 'v1']
            {
                ni[5],
                in[3],
                in[1],
            },
            //  ['v3', 'v2', 'v1', 'v0']
            {
                in[3],
                in[2],
                in[1],
                in[0],
            },
            //  ['!v5', 'v3', 'v0']
            {
                ni[5],
                in[3],
                in[0],
            },
            //  ['!v5', 'v3', 'v2']
            {
                ni[5],
                in[3],
                in[2],
            },
            //  ['!v6', 'v3']
            {
                ni[6],
                in[3],
            },
            //  ['!v6', 'v2']
            {
                ni[6],
                in[2],
            },
        },
        {
            //  ['v5', '!v4', 'v2', '!v1', 'v0']
            {
                in[5],
                ni[4],
                in[2],
                ni[1],
                in[0],
            },
            //  ['v6', 'v5', 'v4', 'v2', '!v1']
            {
                in[6],
                in[5],
                in[4],
                in[2],
                ni[1],
            },
            //  ['!v5', '!v2', '!v1', '!v0']
            {
                ni[5],
                ni[2],
                ni[1],
                ni[0],
            },
            //  ['v6', 'v5', 'v2', '!v0']
            {
                in[6],
                in[5],
                in[2],
                ni[0],
            },
            //  ['v5', '!v2', 'v1', 'v0']
            {
                in[5],
                ni[2],
                in[1],
                in[0],
            },
            //  ['!v5', 'v2', 'v0']
            {
                ni[5],
                in[2],
                in[0],
            },
            //  ['!v5', 'v2', 'v1']
            {
                ni[5],
                in[2],
                in[1],
            },
            //  ['!v6', '!v2']
            {
                ni[6],
                ni[2],
            },
        },
        {
            //  ['v5', '!v4', 'v2', '!v1', 'v0']
            {
                in[5],
                ni[4],
                in[2],
                ni[1],
                in[0],
            },
            //  ['v6', 'v5', '!v1', 'v0']
            {
                in[6],
                in[5],
                ni[1],
                in[0],
            },
            //  ['!v5', '!v1', '!v0']
            {
                ni[5],
                ni[1],
                ni[0],
            },
            //  ['!v5', 'v1', 'v0']
            {
                ni[5],
                in[1],
                in[0],
            },
            //  ['v5', 'v1', '!v0']
            {
                in[5],
                in[1],
                ni[0],
            },
            //  ['!v6', 'v1']
            {
                ni[6],
                in[1],
            },
        },
        {
            //  ['v4', 'v3', 'v1', 'v0']
            {
                in[4],
                in[3],
                in[1],
                in[0],
            },
            //  ['!v6', 'v4', 'v0']
            {
                ni[6],
                in[4],
                in[0],
            },
            //  ['v6', '!v0']
            {
                in[6],
                ni[0],
            },
        },
    };
    invalid = lc_.or_of_and(exp[0]);
    for (size_t i = 0; i < 6; ++i) {
      out[5 - i] = lc_.or_of_and(exp[i + 1]);
    }
  }

 private:
  const LogicCircuit& lc_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_BASE64_DECODE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_SCHEDULE_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_SCHEDULE_H_

#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <memory>
#include <vector>

#include "algebra/compare.h"
#include "arrays/affine.h"
#include "circuits/compiler/node.h"
#include "sumcheck/circuit.h"
#include "sumcheck/quad.h"
#include "util/ceildiv.h"
#include "util/panic.h"

namespace proofs {
template <class Field>
class Scheduler {
  using Elt = typename Field::Elt;
  using nodeinfo = NodeInfoF<Field>;
  using node = NodeF<Field>;
  using size_t_for_storage = term::size_t_for_storage;
  using quad_corner_t = typename Quad<Field>::quad_corner_t;

  const Field& f_;
  const std::vector<node>& nodes_;

 public:
  size_t nwires_;
  size_t nquad_terms_;
  size_t nwires_overhead_;

  Scheduler(const std::vector<node>& nodes, const Field& f)
      : f_(f),
        nodes_(nodes),
        nwires_(0),
        nquad_terms_(0),
        nwires_overhead_(0) {}

  std::unique_ptr<Circuit<Field>> mkcircuit(const std::vector<Elt>& constants,
                                            size_t depth_ub, size_t nc) {
    std::unique_ptr<Circuit<Field>> c = std::make_unique<Circuit<Field>>();

    // number of layers and copies
    c->nl = depth_ub - 1;  // depth 0 = input nodes, not a "layer"
    c->nc = nc;
    c->logc = lg(nc);

    auto lnodes = order_by_layer(constants, depth_ub);

    // TODO [matteof 2025-03-12] ASSIGN_WIRE_IDS() renames LNODES in
    // order to sort it and assign LNODES[].DESIRED_WIRE ID.  Then it
    // throws away the renamed LNODES.  Then FILL_LAYERS() renames
    // LNODES again in order to produce the final quad.  It would be
    // better to produce the quad directly in ASSIGN_WIRE_IDS().  Punt
    // for now, this is just a performance optimization of the
    // compiler anyway.
    //
    assign_wire_ids(lnodes);
    fill_layers(c.get(), depth_ub, lnodes);

    return c;
  }

 private:
  // per-layer representation of nodes and terms
  struct lterm {
    Elt k;
    quad_corner_t lop0, lop1;
  };
  struct lnode {
    quad_corner_t desired_wire_id;

    // Copy wires are forced to be distinct from wires in the
    // original dag, in order to avoid ambiguity in renaming.
    //
    // Copy wires are always of the form 1*op, which doesn't
    // normally appear in the dag because the algebraic simplifier
    // reduces it to op.  However, one can in theory create such
    // a node by judicious use of linear().  Rather than
    // trying to figure out which circuits one is not allowed
    // to write, it seems simpler to just handle this case
    // uniformly.
    bool is_copy_wire;

    std::vector<lterm> lterms;

    lnode(quad_corner_t desired_wire_id, bool is_copy_wire,
          const std::vector<lterm>& lterms)
        : desired_wire_id(desired_wire_id),
          is_copy_wire(is_copy_wire),
          lterms(lterms) {}
  };

  quad_corner_t lop_of_op_at_depth(
      const std::vector<std::vector<quad_corner_t>>& lop, size_t op,
      size_t d) const {
    const node& n = nodes_.at(op);
    return lop.at(op).at(d - n.info.depth);
  }

  // Convert the DAG of nodes into a layered dag of lnodes.
  std::vector<std::vector<lnode>> order_by_layer(
      const std::vector<Elt>& constants, size_t depth_ub) {
    // The source DAG is indexed by NODES_[OP].
    // The destination dag uses a two-dimensional indexing
    // scheme LNODES[D][LOP], where D is the depth.

    // A single value NODES_[OP] may be replicated multiple times in
    // LNODES.  The mapping is maintained in array LOPS such that
    // LOPS[OP][D - D0] contains the LOP index of node OP at depth D.
    // D0 is the depth at which NODES_[OP] is first computed, and
    // there is no point in storing LOPS[OP] for D < D0.

    std::vector<std::vector<lnode>> lnodes(depth_ub);
    std::vector<std::vector<quad_corner_t>> lops(nodes_.size());

    nwires_overhead_ = 0;

    for (size_t op = 0; op < nodes_.size(); ++op) {
      const auto& n = nodes_[op];
      const nodeinfo& nfo = n.info;
      if (nfo.is_needed && !n.zero()) {
        size_t d = nfo.depth;

        // Allocate the LOP at depth D
        quad_corner_t lop = quad_corner_t(lnodes.at(d).size());
        lops.at(op).push_back(lop);

        // create a LOPS entry for depth D
        /*scope*/ {
          std::vector<lterm> lterms;
          for (const auto& t : n.terms) {
            lterm lt = {
                .k = constants.at(t.ki),
                .lop0 = lop_of_op_at_depth(lops, t.op0, d - 1),
                .lop1 = lop_of_op_at_depth(lops, t.op1, d - 1),
            };
            lterms.push_back(lt);
          }
          lnodes.at(d).push_back(lnode(nfo.desired_wire_id(d, depth_ub),
                                       /*is_copy_wire=*/false, lterms));
        }

        // create copy wires
        for (d = nfo.depth + 1; d < nfo.max_needed_depth; ++d) {
          quad_corner_t lop_dm1 = lop;

          // allocate the LOP at depth D
          lop = quad_corner_t(lnodes.at(d).size());
          lops.at(op).push_back(lop);

          std::vector<lterm> lterms;

          // Insert a multiplication by one of the layer
          // at the previous layer.
          lterm lt = {
              .k = f_.one(),
              .lop0 = quad_corner_t(0),
              .lop1 = lop_dm1,
          };
          lterms.push_back(lt);
          lnodes.at(d).push_back(lnode(nfo.desired_wire_id(d, depth_ub),
                                       /*is_copy_wire=*/true, lterms));
          ++nwires_overhead_;
        }  // for copy wires
      }  // if needed
    }  // for OP

    return lnodes;
  }

  //------------------------------------------------------------
  // canonical assignment of wire ids
  //------------------------------------------------------------
  //
  // The canonicalization order is a matter of convention.
  // We make some arbitrary choices that appear to interact
  // better with ZSTD compression.  The label [ARBITRARY CHOICE]
  // denotes all places in the code where this occurs.
  //
  class renamed_lterm {
   public:
    Elt k_;
    quad_corner_t rlop0_, rlop1_;

    // [ARBITRARY CHOICE] Consistent with corner::canonicalize() in
    // sumcheck/quad.h
    renamed_lterm(const Elt& k, quad_corner_t rlop0, quad_corner_t rlop1)
        : k_(k),
          rlop0_(std::min<quad_corner_t>(rlop0, rlop1)),
          rlop1_(std::max<quad_corner_t>(rlop0, rlop1)) {}

    bool operator==(const renamed_lterm& y) const {
      return rlop0_ == y.rlop0_ && rlop1_ == y.rlop1_ && k_ == y.k_;
    }

    // canonical order
    static bool compare(const renamed_lterm& a, const renamed_lterm& b,
                        const Field& F) {
      if (a.rlop0_ < b.rlop0_) return true;
      if (a.rlop0_ > b.rlop0_) return false;
      if (a.rlop1_ < b.rlop1_) return true;
      if (a.rlop1_ > b.rlop1_) return false;
      return elt_less_than(a.k_, b.k_, F);
    }
  };

  class renamed_lnode {
   public:
    quad_corner_t desired_wire_id_;
    quad_corner_t original_wire_index_;
    bool is_copy_wire_;
    std::vector<renamed_lterm> rlterms_;

    renamed_lnode(quad_corner_t desired_wire_id,
                  quad_corner_t original_wire_index, bool is_copy_wire,
                  const std::vector<renamed_lterm>& rlterms)
        : desired_wire_id_(desired_wire_id),
          original_wire_index_(original_wire_index),
          is_copy_wire_(is_copy_wire),
          rlterms_(rlterms) {}

    bool operator==(const renamed_lnode& y) const {
      if (is_copy_wire_ != y.is_copy_wire_) return false;
      if (rlterms_.size() != y.rlterms_.size()) return false;
      size_t l = rlterms_.size();
      for (size_t i = 0; i < l; ++i) {
        if (!(rlterms_[i] == y.rlterms_[i])) return false;
      }
      return true;
    }

    // canonical order
    static bool compare(const renamed_lnode& ra, const renamed_lnode& rb,
                        const Field& F) {
      // Defined before undefined.  This choice is mandated by the
      // fact that the range of defined wire id's starts at 0.
      if (ra.desired_wire_id_ != nodeinfo::kWireIdUndefined) {
        if (rb.desired_wire_id_ != nodeinfo::kWireIdUndefined) {
          return ra.desired_wire_id_ < rb.desired_wire_id_;
        } else {
          return true;
        }
      } else {
        if (rb.desired_wire_id_ != nodeinfo::kWireIdUndefined) {
          return false;
        }
        // else both undefined
      }

      // [ARBITRARY CHOICE] Lexicographic order on the reverse of the
      // terms array.  This seems to compress much better than
      // the normal lexicographic order.
      for (size_t ia = ra.rlterms_.size(), ib = rb.rlterms_.size();
           ia-- > 0 && ib-- > 0;) {
        const renamed_lterm& rlta = ra.rlterms_[ia];
        const renamed_lterm& rltb = rb.rlterms_[ib];
        if (renamed_lterm::compare(rlta, rltb, F)) return true;
        if (renamed_lterm::compare(rltb, rlta, F)) return false;
      }

      // [ARBITRARY CHOICE] If the common suffixes are the same, the
      // shorter terms come first.
      if (ra.rlterms_.size() < rb.rlterms_.size()) return true;
      if (ra.rlterms_.size() > rb.rlterms_.size()) return false;

      // Nodes that were in the original dag come first.
      if (!ra.is_copy_wire_ && rb.is_copy_wire_) return true;
      if (!rb.is_copy_wire_ && ra.is_copy_wire_) return false;

      // equal, i.e., not less-than
      return false;
    }
  };

  template <class T>
  bool uniq(const std::vector<T>& sorted) {
    for (size_t i = 0; i + 1 < sorted.size(); ++i) {
      if (sorted[i] == sorted[i + 1]) return false;
    }
    return true;
  }

  void assign_wire_ids(std::vector<std::vector<lnode>>& lnodes) {
    // all inputs are expected to be defined already
    assert_all_desired_wire_id_defined(lnodes.at(0));

    for (size_t d = 1; d < lnodes.size(); ++d) {
      const std::vector<lnode>& lnodes_at_dm1 = lnodes.at(d - 1);
      const std::vector<lnode>& lnodes_at_d = lnodes.at(d);

      // Create a renamed clone of LNODES_AT_D, in which all
      // the LOP's are mapped to their desired wire id's
      // at the previous layer.  We use different types
      // to avoid any possibility of confusion.
      std::vector<renamed_lnode> renamed_at_d;

      quad_corner_t original_wire_index(0);
      for (const lnode& ln : lnodes_at_d) {
        std::vector<renamed_lterm> rlterms;

        // rename all terms
        rlterms.reserve(ln.lterms.size());
        for (const lterm& lt : ln.lterms) {
          rlterms.push_back(renamed_lterm(
              lt.k,
              lnodes_at_dm1.at(static_cast<size_t>(lt.lop0)).desired_wire_id,
              lnodes_at_dm1.at(static_cast<size_t>(lt.lop1)).desired_wire_id));
        }

        // canonicalize the terms order
        std::sort(rlterms.begin(), rlterms.end(),
                  [&](const renamed_lterm& a, const renamed_lterm& b) {
                    return renamed_lterm::compare(a, b, f_);
                  });

        // Terms must be unique, otherwise the canonicalization is
        // ill-defined.  Uniqueness is guaranteed by the algebraic
        // simplifier, but assert it for good measure.
        check(uniq(rlterms), "rlterms not unique");

        renamed_at_d.push_back(renamed_lnode(
            ln.desired_wire_id, original_wire_index, ln.is_copy_wire, rlterms));
        ++original_wire_index;
      }

      check(renamed_at_d.size() == lnodes_at_d.size(),
            "renamed_at_d.size() == lnodes_at_d.size()");

      std::sort(renamed_at_d.begin(), renamed_at_d.end(),
                [&](const renamed_lnode& a, const renamed_lnode& b) {
                  return renamed_lnode::compare(a, b, f_);
                });

      // Nodes must be unique, otherwise the canonicalization is
      // ill-defined.
      check(uniq(renamed_at_d), "renamed_at_d not unique");

      quad_corner_t wid(0);
      std::vector<lnode>& wlnodes_at_d = lnodes.at(d);

      for (const renamed_lnode& ln : renamed_at_d) {
        lnode& lnpi =
            wlnodes_at_d.at(static_cast<size_t>(ln.original_wire_index_));
        if (lnpi.desired_wire_id != nodeinfo::kWireIdUndefined) {
          // We must have computed the same wire id
          check(wid == lnpi.desired_wire_id, "wid == lnpi.desired_wire_id");
        } else {
          lnpi.desired_wire_id = wid;
        }
        wid++;
      }
    }
  }

  void assert_all_desired_wire_id_defined(const std::vector<lnode>& layer) {
    for (const auto& ln : layer) {
      check(ln.desired_wire_id != nodeinfo::kWireIdUndefined,
            "ln.desired_wire_id != kWireIdUndefined");
    }
  }

  void fill_layers(Circuit<Field>* c, size_t depth_ub,
                   const std::vector<std::vector<lnode>>& lnodes) {
    check(depth_ub == lnodes.size(), "depth_ub == lnodes.size()");

    corner_t nv = corner_t(lnodes.at(depth_ub - 1).size());

    nwires_ = nv;
    c->nv = nv;
    c->logv = lg(nv);

    // d-- > 1 (not 0) because depth 0 denotes input nodes, not a layer.
    // Sumcheck counts layers starting from the output, hence the loop
    // counts downwards.
    for (size_t d = depth_ub; d-- > 1;) {
      corner_t nw =
          corner_t(lnodes.at(d - 1).size());  // inputs[d] == outputs[d-1]
      nwires_ += nw;
      c->l.push_back(
          Layer<Field>{.nw = nw,
                       .logw = lg(nw),
                       .quad = mkquad(lnodes.at(d), lnodes.at(d - 1))});
    }
  }

  std::unique_ptr<const Quad<Field>> mkquad(
      const std::vector<lnode>& lnodes0,  // wires at this layer
      const std::vector<lnode>& lnodes1   // wires at the previous layer
  ) {
    size_t nterms0 = 0;
    for (const auto& ln0 : lnodes0) {
      nterms0 += ln0.lterms.size();
    }
    nquad_terms_ += nterms0;

    auto S = std::make_unique<Quad<Field>>(nterms0);
    size_t i = 0;
    for (const auto& ln0 : lnodes0) {
      for (const auto& lt : ln0.lterms) {
        S->c_[i++] = typename Quad<Field>::corner{
            .g = ln0.desired_wire_id,
            .h = {lnodes1.at(static_cast<size_t>(lt.lop0)).desired_wire_id,
                  lnodes1.at(static_cast<size_t>(lt.lop1)).desired_wire_id},
            .v = lt.k};
      }
    }
    S->canonicalize(f_);
    return S;
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_SCHEDULE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_ZK_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_ZK_H_

#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

// This package implements C interfaces that allow external programs to call
// the zk mdoc-based prover and verifier.
//
// It also contains a helper method that produces a byte representation
// of a circuit which verifies the mdoc with regards to specific properties,
// for example age_over_18. The circuit generation can be run once, and the
// result cached for subsequent use in the prover and verifier.

enum CborAttributeType { kPrimitive, kString, kBytes, kDate, kInt };

/* This struct allows a verifier to express which attribute and value the prover
 * must claim. */
typedef struct {
  uint8_t id[32];
  uint8_t value[64];
  size_t id_len, value_len;
  CborAttributeType type;
} RequestedAttribute;

// Return codes for the run_mdoc2_prover method.
typedef enum {
  MDOC_PROVER_SUCCESS = 0,
  MDOC_PROVER_NULL_INPUT,
  MDOC_PROVER_INVALID_INPUT,
  MDOC_PROVER_CIRCUIT_PARSING_FAILURE,
  MDOC_PROVER_HASH_PARSING_FAILURE,
  MDOC_PROVER_WITNESS_CREATION_FAILURE,
  MDOC_PROVER_GENERAL_FAILURE,
  MDOC_PROVER_MEMORY_ALLOCATION_FAILURE,
  MDOC_PROVER_INVALID_ZK_SPEC_VERSION,
} MdocProverErrorCode;

// Return codes for the run_mdoc2_verifier method.
typedef enum {
  MDOC_VERIFIER_SUCCESS = 0,
  MDOC_VERIFIER_CIRCUIT_PARSING_FAILURE,
  MDOC_VERIFIER_PROOF_TOO_SMALL,
  MDOC_VERIFIER_HASH_PARSING_FAILURE,
  MDOC_VERIFIER_SIGNATURE_PARSING_FAILURE,
  MDOC_VERIFIER_GENERAL_FAILURE,
  MDOC_VERIFIER_NULL_INPUT,
  MDOC_VERIFIER_INVALID_INPUT,
  MDOC_VERIFIER_ARGUMENTS_TOO_SMALL,
  MDOC_VERIFIER_ATTRIBUTE_NUMBER_MISMATCH,
  MDOC_VERIFIER_INVALID_ZK_SPEC_VERSION,
} MdocVerifierErrorCode;

// Return codes for the generate_circuit method.
typedef enum {
  CIRCUIT_GENERATION_SUCCESS = 0,
  CIRCUIT_GENERATION_NULL_INPUT,
  CIRCUIT_GENERATION_ZLIB_FAILURE,
  CIRCUIT_GENERATION_GENERAL_FAILURE,
  CIRCUIT_GENERATION_INVALID_ZK_SPEC_VERSION,
} CircuitGenerationErrorCode;

// This structure represents a version of ZK specification supported by this
// library. It is passed into all the methods for circuit generation, running
// the prover and verifier.
// It allows us to version the specification of the ZK system. The prover and
// the verifier are supposed to negotiate the version of the specification they
// both support before executing digital credential presentment.
typedef struct {
  // The ZK system name and version- "longfellow-libzk-v*" for Google library.
  const char* system;
  // The hash of the compressed circuit (the way it's generated and passed to
  // prover/verifier)
  const char circuit_hash[65];
  // The number of attributes that the circuit supports.
  size_t num_attributes;
  // The version of the ZK specification.
  size_t version;
} ZkSpecStruct;

static const char kDefaultDocType[] = "org.iso.18013.5.1.mDL";

// The run_mdoc2_prover method takes byte-oriented inputs that describe a
// circuit, mdoc, the public key of the issuer for the mdoc, a transcript
// for the mdoc request operation, an array of RequestedAttribute that
// represents claims that you want to prove, and a 20-char representation of the
// current time. It writes the proof and its length into the input parameter prf
// and proof_len. It is the responsibility of the caller to later free the proof
// memory. If the prover fails to produce a proof, e.g., because the mdoc is
// invalid, or the now time does not satisfy the validFrom and validUntil
// constraints, then the prover returns an error code.
// The following lines document how attributes can be opened in ZK.
// {(uint8_t *)"family_name", 11, (uint8_t *)"Mustermann", 10},
// {(uint8_t *)"height", 6, (uint8_t *)"\x18\xaf", 2},
// {(uint8_t *)"birth_date", 10, (uint8_t *)"\xD9\x03\xEC\x6A" "1971-09-01",
// 14},
// {(uint8_t *)"issue_date", 10, (uint8_t *)"\xD9\x03\xEC\x6A" "2024-03-15",
// 14},
MdocProverErrorCode run_mdoc_prover(
    const uint8_t* bcp, size_t bcsz,          /* circuit data */
    const uint8_t* mdoc, size_t mdoc_len,     /* full mdoc */
    const char* pkx, const char* pky,         /* string rep of public key */
    const uint8_t* transcript, size_t tr_len, /* session transcript */
    const RequestedAttribute* attrs, size_t attrs_len,
    const char* now, /* time formatted as "2023-11-02T09:00:00Z" */
    uint8_t** prf, size_t* proof_len, const ZkSpecStruct* zk_spec_version);

// The run_mdoc2_verifier method accepts a byte representation of the circuit,
// the public key of the issuer, the transcript, an array of RequestedAttribute
// that represents claims that you want to verify, and a 20-char representation
// of the time, as well as the proof and its length.
MdocVerifierErrorCode run_mdoc_verifier(
    const uint8_t* bcp, size_t bcsz,          /* circuit data */
    const char* pkx, const char* pky,         /* string rep of public key */
    const uint8_t* transcript, size_t tr_len, /* session transcript */
    const RequestedAttribute* attrs, size_t attrs_len,
    const char* now, /* time formatted as "2023-11-02T09:00:00Z" */
    const uint8_t* zkproof, size_t proof_len, const char* docType,
    const ZkSpecStruct* zk_spec_version);

// Produces a compressed version of the circuit bytes for the specified number
// of attributes.
CircuitGenerationErrorCode generate_circuit(const ZkSpecStruct* zk_spec_version,
                                            uint8_t** cb, size_t* clen);

// Produces an identifier for a pair of circuits (c_1, c_2) over (Fp256, f_128)
// respectively. This method parses the input bytes into two circuits, computes
// the circuit's ids of each, and then computes the SHA256 hash of the two ids.
// This method is used to identify "circuit bundles" consisting of multiple
// circuits.
int circuit_id(uint8_t id[/*kSHA256DigestSize*/], const uint8_t* bcp,
               size_t bcsz, const ZkSpecStruct* zk_spec);

enum { kNumZkSpecs = 12 };
// This is a hardcoded list of all the ZK specifications supported by this
// library. Every time a new breaking change is introduced in either the circuit
// format or its interpretation, a new version must be added here.
// It is possible to remove old versions, if we're sure that they are not used
// by either provers of verifiers in the wild.
extern const ZkSpecStruct kZkSpecs[kNumZkSpecs];

// Returns a static pointer to the ZkSpecStruct that matches the given system
// name and circuit hash. Returns nullptr if no matching ZkSpecStruct is found.
const ZkSpecStruct* find_zk_spec(const char* system_name,
                                 const char* circuit_hash);

#ifdef __cplusplus
}
#endif

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_ZK_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_EC_ELLIPTIC_CURVE_H_
#define PRIVACY_PROOFS_ZK_LIB_EC_ELLIPTIC_CURVE_H_

#include <cstddef>
#include <cstdint>

#include "algebra/nat.h"
#include "util/panic.h"

namespace proofs {
// Elliptic curve class that supports basic operations such as addition,
// doubling. The algorithms are described in
// https://eprint.iacr.org/2015/1060.pdf.
// The const Field parameter is meant as a type check to keep different
// elliptic curves from interacting. The convention is to use the last 5
// digits of the coordinate field prime in base-10 to name a curve.
// The kN template parameter describes the number of bits in the curve
// order, e.g., to handle curves like P-521, K-283, etc.
template <class Field_, size_t W, size_t kN>
class EllipticCurve {
 public:
  using Field = Field_;
  using Elt = typename Field::Elt;
  using N = Nat<W>;

  static constexpr const size_t kBits = kN; /* # bits in size of the group */

  const Field& f_;
  Elt a_;
  Elt b_;
  Elt gx_, gy_, gz_;  // generator of the group
  const Elt k2, k3, k8, k3b, k9b, k24b;

  struct ECPoint {
    Elt x;
    Elt y;
    Elt z;

    ECPoint() = default;
    ECPoint(const Elt& x, const Elt& y, const Elt& z) : x(x), y(y), z(z) {}
  };

  EllipticCurve(const Elt& a, const Elt& b, const Elt& gX, const Elt& gY,
                const Field_& F)
      : f_(F),
        a_(a),
        b_(b),
        gx_(gX),
        gy_(gY),
        gz_(F.one()),
        k2(F.of_scalar(2)),
        k3(F.of_scalar(3)),
        k8(F.of_scalar(8)),
        k3b(F.mulf(k3, b_)),
        k9b(F.mulf(F.of_scalar(9), b_)),
        k24b(F.mulf(F.of_scalar(24), b_)) {
    is_minus_3_a_ = (a_ == F.negf(k3));
    is_zero_a_ = (a_ == F.zero());
  }

  // This equality method makes no assumptions about whether the inputs
  // are valid points on the curve. Just verifying cross-mult is not
  // enough if one of the points is invalid. This method is not constant
  // time and can return early if any point is infinity.
  bool equal(const ECPoint& p, const ECPoint& q) const {
    // handle inf point, then point equality, and finally projective eq
    if (q.x == f_.zero() && q.z == f_.zero() && q.y != f_.zero() &&
        p.x == f_.zero() && p.z == f_.zero() && p.y != f_.zero()) {
      return true;
    }
    if (q.x == p.x && q.z == p.z && q.y == p.y) {
      return true;
    }

    return (f_.mulf(p.x, q.z) == f_.mulf(q.x, p.z) &&
            f_.mulf(p.y, q.z) == f_.mulf(q.y, p.z));
  }

  // This method assumes a point is either zero or has z=1 coordinate,
  // so it does not implement the full mathematical notion of Jacobian-form
  // ec point.
  bool is_on_curve(const ECPoint& p) const {
    if (equal(p, zero())) {
      return true;
    }
    // Do not support Jacobian coordinate with z != 1"
    if (p.z != f_.one()) {
      return false;
    }
    return is_on_curve(p.x, p.y);
  }

  // This caller of the constructor must first verify that (x,y) is on the
  // curve using the isOnCurve() method.
  ECPoint point(const Elt& x, const Elt& y) const {
    ECPoint p(x, y, f_.one());
    check(is_on_curve(p), "Invalid curve point");
    return p;
  }

  void normalize(ECPoint& p) const {
    if (p.z == f_.zero()) return;
    f_.invert(p.z);
    f_.mul(p.x, p.z);
    f_.mul(p.y, p.z);
    p.z = f_.one();
  }

  void addE(ECPoint& p3, const ECPoint& p2) const {
    addE(p3.x, p3.y, p3.z, p3.x, p3.y, p3.z, p2.x, p2.y, p2.z);
  }

  void doubleE(ECPoint& p3) const {
    doubleE(p3.x, p3.y, p3.z, p3.x, p3.y, p3.z);
  }

  // Functional interface.
  ECPoint addEf(ECPoint p1, const ECPoint& p2) const {
    addE(p1, p2);
    return p1;
  }

  ECPoint doubleEf(ECPoint p1) const {
    doubleE(p1);
    return p1;
  }

  // Computes the elliptic curve point p * scalar.
  // This method is not constant time, but that is not necessary in the current
  // zk implementation.
  ECPoint scalar_multf(const ECPoint& p, const N& scalar) const {
    ECPoint x = p;
    ECPoint p3 = zero();
    for (size_t d = 0; d < N::kLimbs; ++d) {
      auto nd = scalar.limb_[d];
      for (size_t i = 0; i < N::kBitsPerLimb; ++i) {
        if (nd & 1) {
          addE(p3, x);
        }
        doubleE(x);
        nd >>= 1;
      }
    }
    return p3;
  }

  // Computes the multi-scalar elliptic curve point multiplication.
  // Input: p1, p2, ..., pn, and scalars s1, s2, ..., sn
  // Output: p1 * s1 + p2 * s2 + ... + pn * sn
  // This method is not a constant time operation.
  ECPoint scalar_multf(size_t n, ECPoint p[/*n*/], N scalar[/*n*/]) const {
    if (n == 0) {
      return zero();
    } else if (n == 1) {
      return scalar_multf(p[0], scalar[0]);
    } else {
      return bos_coster(n, p, scalar);
    }
  }

  ECPoint zero() const { return ECPoint(f_.zero(), f_.one(), f_.zero()); }
  ECPoint generator() const { return ECPoint(gx_, gy_, gz_); }

  // Check whether Y^2 = X^3 + aX + b.
  bool is_on_curve(const Elt& X, const Elt& Y) const {
    Elt left = f_.mulf(Y, Y);
    Elt X3 = f_.mulf(X, f_.mulf(X, X));
    Elt right = f_.addf(f_.addf(X3, f_.mulf(a_, X)), b_);
    return left == right;
  }

  void addE(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X1, const Elt& Y1,
            const Elt& Z1, const Elt& X2, const Elt& Y2, const Elt& Z2) const {
    // Optimized special cases.
    if (is_zero_a_) return addEZeroA(X3o, Y3o, Z3o, X1, Y1, Z1, X2, Y2, Z2);
    if (is_minus_3_a_)
      return addEMinus3A(X3o, Y3o, Z3o, X1, Y1, Z1, X2, Y2, Z2);

    /*
    Source: 1998 Cohen–Miyaji–Ono "Efficient elliptic curve exponentiation using
    mixed coordinates", formula (3), plus common-subexpression elimination.
    These equations are taken from the Hyperelliptic curve formula database.
    This could have special short-cuts for addition with inf and self-addition,
    which speeds up all multi-exponentiation computations that involve a lot of
    small exponents.
    */
    if (X1 == f_.zero() && Z1 == f_.zero()) {
      X3o = X2;
      Y3o = Y2;
      Z3o = Z2;
      return;
    }

    if (X2 == f_.zero() && Z2 == f_.zero()) {
      X3o = X1;
      Y3o = Y1;
      Z3o = Z1;
      return;
    }

    Elt Y1Z2 = f_.mulf(Y1, Z2);
    Elt X1Z2 = f_.mulf(X1, Z2);
    Elt u = f_.subf(f_.mulf(Y2, Z1), Y1Z2);
    Elt v = f_.subf(f_.mulf(X2, Z1), X1Z2);
    if (u == f_.zero()) {
      doubleE(X3o, Y3o, Z3o, X1, Y1, Z1);
      return;
      // Self addition, invoke Double method.
    }
    /* This check occurs after the u check.
    If u!=0, but v=0, then the points are inverses.
    */
    if (v == f_.zero()) {
      X3o = f_.zero();
      Y3o = f_.one();
      Z3o = f_.zero();
      return;
    }

    Elt Z1Z2 = f_.mulf(Z1, Z2);
    Elt uu = f_.mulf(u, u);
    Elt vv = f_.mulf(v, v);
    Elt vvv = f_.mulf(v, vv);
    Elt R = f_.mulf(vv, X1Z2);
    Elt A = f_.subf(f_.subf(f_.mulf(uu, Z1Z2), vvv), f_.mulf(k2, R));
    Elt X3 = f_.mulf(v, A);
    Elt Y3 = f_.subf(f_.mulf(u, f_.subf(R, A)), f_.mulf(vvv, Y1Z2));
    Elt Z3 = f_.mulf(vvv, Z1Z2);

    X3o = X3;
    Y3o = Y3;
    Z3o = Z3;
  }

  void doubleE(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X, const Elt& Y,
               const Elt& Z) const {
    // Optimized special cases.
    if (is_zero_a_) return doubleEZeroA(X3o, Y3o, Z3o, X, Y, Z);
    if (is_minus_3_a_) return doubleEMinus3A(X3o, Y3o, Z3o, X, Y, Z);

    /*
    // 1998 Cohen–Miyaji–Ono "Efficient elliptic curve exponentiation using
    mixed coordinates", formula (4), This version of the double formula trades
    general mults for mults by 2,4,8 which can be implemented with additions.
    This results in savings of 200ns on double.
    */
    if (X == f_.zero() && Z == f_.zero()) {
      X3o = X;
      Y3o = f_.one();
      Z3o = Z;
      return;
    }

    Elt Z2 = f_.mulf(Z, Z);
    Elt X2 = f_.mulf(X, X);
    Elt X2_3 = f_.addf(f_.addf(X2, X2), X2);
    Elt s = f_.mulf(Y, Z);
    Elt ss = f_.mulf(s, s);
    Elt sss = f_.mulf(s, ss);
    Elt sss_2 = f_.addf(sss, sss);
    Elt w = f_.addf(f_.mulf(a_, Z2), X2_3);
    Elt R = f_.mulf(Y, s);
    Elt sss_4 = f_.addf(sss_2, sss_2);
    Elt B = f_.mulf(X, R);
    Elt sss_8 = f_.addf(sss_4, sss_4);
    Elt B_2 = f_.addf(B, B);
    Elt R2 = f_.mulf(R, R);
    Elt B_4 = f_.addf(B_2, B_2);
    Elt B_8 = f_.addf(B_4, B_4);
    Elt w2 = f_.mulf(w, w);
    Elt h = f_.subf(w2, B_8);
    Elt s_2 = f_.addf(s, s);
    Elt X3 = f_.mulf(h, s_2);
    Elt R2_2 = f_.addf(R2, R2);
    Elt R2_4 = f_.addf(R2_2, R2_2);
    Elt R2_8 = f_.addf(R2_4, R2_4);
    Elt Y3 = f_.subf(f_.mulf(w, f_.subf(B_4, h)), R2_8);
    Elt Z3 = sss_8;

    X3o = X3;
    Y3o = Y3;
    Z3o = Z3;
  }

 private:
  /* From Algorithm 7: Complete, projective point addition for prime order
    j-invariant 0 short Weierstrass curves E/Fq : y^2 = x^3 + b.

    X3 = (X1 Y2 + X2 Y1)(Y1 Y2 - 3b Z1 Z2) - 3b(Y1 Z2 + Y2 Z1)(X1 Z2 + X2 Z1)
    Y3 = (Y1 Y2 + 3b Z1 Z2)(Y1 Y2 - 3b Z1 Z2) + 9b X1 X2 (X1 Z2 + X2 Z1)
    Z3 = (Y1 Z2 + Y2 Z1)(Y1 Y2 + 3b Z1 Z2) + 3 X1 X2(X1 Y2 + X2 Y1)
   */
  void addEZeroA(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X1, const Elt& Y1,
                 const Elt& Z1, const Elt& X2, const Elt& Y2,
                 const Elt& Z2) const {
    Elt t0 = f_.mulf(X2, Y1);
    Elt t1 = f_.mulf(X1, Y2);
    Elt t2 = f_.addf(t1, t0);
    Elt t3 = f_.mulf(Y1, Y2);
    Elt t4 = f_.mulf(Z1, Z2);
    Elt t5 = f_.mulf(Y1, Z2);
    Elt t6 = f_.mulf(Y2, Z1);
    Elt t7 = f_.addf(t5, t6);
    Elt t8 = f_.mulf(X1, Z2);
    Elt t9 = f_.mulf(X2, Z1);
    Elt t10 = f_.addf(t8, t9);
    Elt t11 = f_.mulf(X1, X2);
    Elt t12 = f_.mulf(k3b, t4);
    Elt t13 = f_.addf(t3, t12);
    Elt t14 = f_.subf(t3, t12);

    X3o = f_.subf(f_.mulf(t2, t14), f_.mulf(k3b, f_.mulf(t7, t10)));
    Y3o = f_.addf(f_.mulf(t13, t14), f_.mulf(k9b, f_.mulf(t11, t10)));
    Z3o = f_.addf(f_.mulf(t7, t13), f_.mulf(k3, f_.mulf(t11, t2)));
  }

  /*Algorithm 4: Complete, projective point addition for prime order short
   * Weierstrass curves E/Fq : y^2 = x^33 + ax + b with a = −3.
   */
  void addEMinus3A(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X1, const Elt& Y1,
                   const Elt& Z1, const Elt& X2, const Elt& Y2,
                   const Elt& Z2) const {
    Elt t0 = f_.mulf(X1, X2);
    Elt t1 = f_.mulf(Y1, Y2);
    Elt t2 = f_.mulf(Z1, Z2);
    Elt t3 = f_.addf(X1, Y1);
    Elt t4 = f_.addf(X2, Y2);
    t3 = f_.mulf(t3, t4);
    t4 = f_.addf(t0, t1);
    t3 = f_.subf(t3, t4);
    t4 = f_.addf(Y1, Z1);
    Elt X3 = f_.addf(Y2, Z2);
    t4 = f_.mulf(t4, X3);
    X3 = f_.addf(t1, t2);
    t4 = f_.subf(t4, X3);
    X3 = f_.addf(X1, Z1);
    Elt Y3 = f_.addf(X2, Z2);
    X3 = f_.mulf(X3, Y3);
    Y3 = f_.addf(t0, t2);
    Y3 = f_.subf(X3, Y3);
    Elt Z3 = f_.mulf(b_, t2);
    X3 = f_.subf(Y3, Z3);
    Z3 = f_.addf(X3, X3);
    X3 = f_.addf(X3, Z3);
    Z3 = f_.subf(t1, X3);
    X3 = f_.addf(t1, X3);
    Y3 = f_.mulf(b_, Y3);
    t1 = f_.addf(t2, t2);
    t2 = f_.addf(t1, t2);
    Y3 = f_.subf(Y3, t2);
    Y3 = f_.subf(Y3, t0);
    t1 = f_.addf(Y3, Y3);
    Y3 = f_.addf(t1, Y3);
    t1 = f_.addf(t0, t0);
    t0 = f_.addf(t1, t0);
    t0 = f_.subf(t0, t2);
    t1 = f_.mulf(t4, Y3);
    t2 = f_.mulf(t0, Y3);
    Y3 = f_.mulf(X3, Z3);
    Y3 = f_.addf(Y3, t2);
    X3 = f_.mulf(t3, X3);
    X3 = f_.subf(X3, t1);
    Z3 = f_.mulf(t4, Z3);
    t1 = f_.mulf(t3, t0);
    Z3 = f_.addf(Z3, t1);

    X3o = X3;
    Y3o = Y3;
    Z3o = Z3;
  }

  /* From Algorithm 6: Exception-free point doubling for prime order short
   * Weierstrass curves E/Fq : y^2 = x^3 + ax + b with a = −3.
   */
  void doubleEMinus3A(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X, const Elt& Y,
                      const Elt& Z) const {
    Elt t0 = f_.mulf(X, X);
    Elt t1 = f_.mulf(Y, Y);
    Elt t2 = f_.mulf(Z, Z);
    Elt t3 = f_.mulf(X, Y);
    t3 = f_.addf(t3, t3);
    Elt Z3 = f_.mulf(X, Z);
    Z3 = f_.addf(Z3, Z3);
    Elt Y3 = f_.mulf(b_, t2);
    Y3 = f_.subf(Y3, Z3);
    Elt X3 = f_.addf(Y3, Y3);
    Y3 = f_.addf(X3, Y3);
    X3 = f_.subf(t1, Y3);
    Y3 = f_.addf(t1, Y3);
    Y3 = f_.mulf(X3, Y3);
    X3 = f_.mulf(X3, t3);
    t3 = f_.addf(t2, t2);
    t2 = f_.addf(t2, t3);
    Z3 = f_.mulf(b_, Z3);
    Z3 = f_.subf(Z3, t2);
    Z3 = f_.subf(Z3, t0);
    t3 = f_.addf(Z3, Z3);
    Z3 = f_.addf(Z3, t3);
    t3 = f_.addf(t0, t0);
    t0 = f_.addf(t3, t0);
    t0 = f_.subf(t0, t2);
    t0 = f_.mulf(t0, Z3);
    Y3 = f_.addf(Y3, t0);
    t0 = f_.mulf(Y, Z);
    t0 = f_.addf(t0, t0);
    Z3 = f_.mulf(t0, Z3);
    X3 = f_.subf(X3, Z3);
    Z3 = f_.mulf(t0, t1);
    Z3 = f_.addf(Z3, Z3);
    Z3 = f_.addf(Z3, Z3);

    X3o = X3;
    Y3o = Y3;
    Z3o = Z3;
  }

  /* From  Algorithm 9: Exception-free point doubling for prime order
    j-invariant 0 short Weierstrass curves E/Fq y^2 = x^3 + b

    X3 = 2XY (YY − 9bZZ)
    Y3 = (YY − 9bZZ)(YY + 3bZZ) + 24bYYZZ
    Z3 = 8YYYZ.
  */
  void doubleEZeroA(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X, const Elt& Y,
                    const Elt& Z) const {
    Elt t0 = f_.mulf(X, Y);
    Elt t1 = f_.mulf(Y, Y);
    Elt t2 = f_.mulf(Z, Z);
    Elt t4 = f_.mulf(Y, Z);
    Elt t5 = f_.mulf(k9b, t2);  // 9bZZ
    Elt t6 = f_.subf(t1, t5);   // YY - 9bZZ
    Elt t7 = f_.mulf(k3b, t2);  // 3bZZ
    Elt t8 = f_.addf(t1, t7);   // YY + 3bZZ
    X3o = f_.mulf(k2, f_.mulf(t0, t6));
    Y3o = f_.addf(f_.mulf(t6, t8), f_.mulf(k24b, f_.mulf(t1, t2)));
    Z3o = f_.mulf(k8, f_.mulf(t1, t4));
  }

  //------------------------------------------------------------
  // Multi-exponentiation SUM_i scalarMult(p[i], s[i])

  // We follow the basic strategy outlined in Daniel J. Bernstein,
  // Niels Duif, Tanja Lange, Peter Schwabe, and Bo-Yin Yang,
  // "High-speed high-security signatures",
  // https://eprint.iacr.org/2011/368, where Bernstein et al. credit
  // the method to Bos and Coster via a reference to Peter de Rooij,
  // "Efficient exponentiation using precomputation and vector
  // addition chains", in Eurocrypt ’94.  In my opinion [matteof@]
  // the method of Bernstein et al. is not quite the same as the papers
  // that they reference, but either way we follow Bernstein et al.
  // with a few modifications, and call the method bos_coster() in
  // this file.

  // The basic idea is to keep the list (p[i], s[i]) of pairs
  // (point, scalar) in descending order of scalar, so that s[0]
  // is the maximum.

  // Bernstein's method repeatedly replaces p[0]*s[0]+p[1]*s[1]
  // by p[0]*(s[0]-s[1])+(p[0]+p[1])*s[1], where now the first
  // scalar becomes smaller.  Eventually all the scalars s[i] become
  // 0 for i>=1.

  // For random scalars, one can roughly expect s[0]-s[1] to be
  // about |F|/n, so the method can be expected to set approximately
  // log n scalar bits to zero in each iteration.  However, its worst
  // case is horrific.  E.g., if s[1]=1 and s[0] is O(2**256), the
  // method will require O(2**256) iterations to converge.

  // To avoid this worst-case behavior, we depart from Bernstein and
  // perform either a Bernstein step or a double-and-add step,
  // whichever one decreases s[0] the most.  A double-and-add writes
  // s[0] = 2*a+b and replaces p[0]*s[0] with (2*p[0])*(s[0]/2) *
  // b*p[0], where the second multiplication is trivial because b \in
  // {0,1}.  With this choice, we are guaranteed to eliminate at least
  // one scalar bit per iteration, so the method is no worse than a
  // loop of single scalar multiplications.  Bernstein et al., page
  // 17, are aware of the problem, but they say that doing anything
  // about it is "not worthwhile".  On the other hand, the fix doesn't
  // cost anything either, so may as well do it.  (Bernstein et
  // al. propose a different fix.)

  // For easy access to the largest s[0] and second-largest s[1], we
  // keep the (p[i], s[i]) terms in a heap.  Here, "heap" denotes a
  // variant of the standard heap where the root node H[0] has one
  // child H[1] (as opposed to two children), and every other node i>0
  // has two children H[2*i] and H[2*i+1].  A heap comprises at least
  // two elements H[0] and H[1].  In this way, the two largest scalars
  // are always available directly.

  // The following function is equivalent to assigning (p[i], s[i]) =
  // (tp, ts) followed by restoring heap order.  However, the logic is
  // a bit complicated by our desire to avoid unnecessary copies of
  // large objects (e.g., swap p[i] with a child, followed by another
  // swap of the child with its child.)
  //
  // Warning: tp and ts MUST NOT be references to p[i] and s[i], since
  // the algorithm overwrites the p and s arrays.  This complication
  // ensues because we are trying to avoid unnecessary copies.
  void bury(size_t i, size_t n, ECPoint p[/*n*/], N s[/*n*/], const ECPoint& tp,
            const N& ts) const {
    while (2 * i < n) {
      // at least one child
      size_t cld = 2 * i;
      if (2 * i + 1 < n && s[cld] < s[2 * i + 1]) {
        // right child exists and is larger
        cld = 2 * i + 1;
      }

      if (ts < s[cld]) {
        // I is out of order with CLD.  Bubble CLD up the tree and
        // continue as in BURY(CLD, N, P, S, TP, TS).
        s[i] = s[cld];
        p[i] = p[cld];
        i = cld;
      } else {
        // already in heap order, stop here.
        break;
      }
    }

    p[i] = tp;
    s[i] = ts;
  }

  // bury (p[0], s[0]) to its rightful place in the heap.
  void bury0(size_t n, ECPoint p[/*n*/], N s[/*n*/]) const {
    if (s[0] < s[1]) {
      // equivalent to swap(s[0], s[1]); bury(s[1]);
      // but without the possibly unnecessary assignment of (s,p)[1]
      ECPoint tp = p[0];
      N ts = s[0];
      p[0] = p[1];
      s[0] = s[1];
      bury(1, n, p, s, tp, ts);
    }
  }

  // The main Bernstein/Bos-Coster algorithm.
  ECPoint bos_coster(size_t n, ECPoint p[/*n*/], N s[/*n*/]) const {
    check(n >= 2, "n >= 2");
    ECPoint res = zero();

    // build a heap on [1..n)
    for (size_t i = /*floor*/ (n / 2); i >= 1; --i) {
      // create temporary copies of p[i], s[i], which are passed by
      // const &.
      bury(i, n, p, s, ECPoint(p[i]), N(s[i]));
    }
    // finish the heap on [0..n)
    bury0(n, p, s);

    while (s[0] != /*zero*/ N{}) {
      // ns0 = s[0] - s[1]
      N ns0(s[0]);
      ns0.sub(s[1]);

      // Double-and-add is (locally) better than Bernstein iff s[0]/2
      // < s[0] - s[1], equivalent to s[0] > 2*s[1], equivalent to
      // ns0 = s[0] - s[1] > s[1]:
      if (s[1] < ns0) {
        // res += (s[0] & 1) * p[0]; s[0] /= 2; p[0] *= 2;
        uint64_t lsb = s[0].shiftr(1);
        if (lsb != 0) {
          addE(res, p[0]);
        }
        doubleE(p[0]);
      } else {
        // s[0] -= s[1], p[1] += p[0]
        s[0] = ns0;
        addE(p[1], p[0]);
      }
      bury0(n, p, s);
    }
    return res;
  }

  bool is_zero_a_;
  bool is_minus_3_a_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_EC_ELLIPTIC_CURVE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_TESTING_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_TESTING_H_

#include <stddef.h>

#include "circuits/cbor_parser/cbor.h"
#include "circuits/cbor_parser/cbor_constants.h"
#include "circuits/cbor_parser/cbor_witness.h"
#include "circuits/logic/evaluation_backend.h"
#include "circuits/logic/logic.h"

// The purpose of this class is to convert the witnesses from Elt to
// EltW.
//
// Why?
//
// We want EltW in the evaluation backend to be a distinct type from
// Elt.  They are really the same thing, but we want to be able to
// instantiate circuits in the compiler backend as well, and thus
// circuits ought not to rely on the fact that EvaluationBackend::EltW
// is really an Elt in disguise.
// Consequently, tests in the evaluation backend must accept EltW.
//
// The witness generator must produce Elt, otherwise this forces the
// inclusion of Logic in the app.  We don't like that because Logic
// is just a set of helpers to generate circuits, and the final app
// is not supposed to generate circuits (since circuits are part of the
// prover<->verifier API and so they must be set in stone in advance.)
//
// So this class is the price to be paid to maintain this typing
// hygiene.  Time will tell whether it was worth it.

namespace proofs {

template <class Field>
class CborTesting {
  using EvalBackend = EvaluationBackend<Field>;
  using LogicF = Logic<Field, EvalBackend>;
  using EltW = typename LogicF::EltW;
  using BitW = typename LogicF::BitW;
  using CborL = Cbor<LogicF>;
  using CborWitnessF = CborWitness<Field>;

 public:
  explicit CborTesting(const Field &F) : f_(F) {}

  void convert_witnesses(
      size_t n, typename CborL::v8 in[/*n*/],
      typename CborL::position_witness pw[/*n*/],
      typename CborL::global_witness &gw,
      const typename CborWitnessF::v8 inS[/*n*/],
      const typename CborWitnessF::position_witness pwS[/*n*/],
      const typename CborWitnessF::global_witness &gwS) const {
    const EvalBackend ebk(f_);
    const LogicF L(&ebk, f_);

    for (size_t i = 0; i < n; ++i) {
      for (size_t j = 0; j < 8; ++j) {
        in[i][j] = BitW(L.konst(inS[i][j]), f_);
      }
      pw[i].encoded_sel_header = L.konst(pwS[i].encoded_sel_header);
    }

    gw.invprod_decode = L.konst(gwS.invprod_decode);
    gw.cc0 = L.konst(gwS.cc0);
    gw.invprod_parse = L.konst(gwS.invprod_parse);
  }

  // Return an index that can be fed to a circuit in the
  // evaluation backend (i.e., a bit vector).
  typename CborL::vindex index(size_t j) const {
    const EvalBackend ebk(f_);
    const LogicF L(&ebk, f_);
    return L.template vbit<CborConstants::kIndexBits>(j);
  }

 private:
  const Field &f_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_TESTING_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP_GENERIC_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP_GENERIC_H_

#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <utility>

#include "algebra/nat.h"
#include "algebra/static_string.h"
#include "algebra/sysdep.h"
#include "util/panic.h"

namespace proofs {
struct PrimeFieldTypeTag {};

/*
The Fp_generic class contains the implementation of a finite field.
*/
template <size_t W64, bool optimized_mul, class OPS>
class FpGeneric {
 public:
  // Type alias for a natural number, and the limbs within the nat are public
  // to allow casting and operations.
  using N = Nat<W64>;
  using limb_t = typename N::limb_t;
  using TypeTag = PrimeFieldTypeTag;

  static constexpr size_t kU64 = N::kU64;
  static constexpr size_t kBytes = N::kBytes;
  static constexpr size_t kSubFieldBytes = kBytes;
  static constexpr size_t kBits = N::kBits;
  static constexpr size_t kLimbs = N::kLimbs;

  static constexpr bool kCharacteristicTwo = false;
  static constexpr size_t kNPolyEvaluationPoints = 6;

  /* The Elt struct represented an element in the finite field.
   */
  struct Elt {
    N n;
    bool operator==(const Elt& y) const { return n == y.n; }
    bool operator!=(const Elt& y) const { return !operator==(y); }
  };

  explicit FpGeneric(const N& modulus) : m_(modulus), negm_(N{}) {
    negm_.sub(m_);

    // compute rawhalf = (m + 1) / 2 = floor(m / 2) + 1 since m is odd
    N raw_half = m_;
    raw_half.shiftr(1);
    raw_half.add(N(1));
    raw_half_ = Elt{raw_half};

    mprime_ = -inv_mod_b(m_.limb_[0]);
    rsquare_ = Elt{N(1)};
    for (size_t bits = 2 * kBits; bits > 0; bits--) {
      add(rsquare_, rsquare_);
    }

    for (uint64_t i = 0; i < sizeof(k_) / sizeof(k_[0]); ++i) {
      // convert k_[i] into montgomery form by calling mul0()
      // directly, since mul() requires k_[0] and k_[1] to be
      // defined
      k_[i] = Elt{N(i)};
      mul0(k_[i], rsquare_);
    }

    mone_ = negf(k_[1]);
    half_ = invertf(k_[2]);

    for (size_t i = 0; i < kNPolyEvaluationPoints; ++i) {
      poly_evaluation_points_[i] = of_scalar(i);
      if (i == 0) {
        inv_small_scalars_[i] = zero();
      } else {
        inv_small_scalars_[i] = invertf(poly_evaluation_points_[i]);
      }
    }
  }

  explicit FpGeneric(const StaticString s) : FpGeneric(N(s)) {}

  template <size_t LEN>
  explicit FpGeneric(const char (&s)[LEN]) : FpGeneric(N(s)) {}

  // Hack: works only if OPS::modulus is defined, and will
  // fail to compile otherwise.
  explicit FpGeneric() : FpGeneric(N(OPS::kModulus)) {}

  FpGeneric(const FpGeneric&) = delete;
  FpGeneric& operator=(const FpGeneric&) = delete;

  template <size_t N>
  Elt of_string(const char (&s)[N]) const {
    return of_charp(&s[0]);
  }

  Elt of_string(const StaticString& s) const { return of_charp(s.as_pointer); }

  std::optional<Elt> of_untrusted_string(const char* s) const {
    auto maybe = N::of_untrusted_string(s);
    if (maybe.has_value() && maybe.value() < m_) {
      return to_montgomery(maybe.value());
    } else {
      return std::nullopt;
    }
  }

  // a += y
  void add(Elt& a, const Elt& y) const {
    if (kLimbs == 1) {
      limb_t aa = a.n.limb_[0], yy = y.n.limb_[0], mm = m_.limb_[0];
      a.n.limb_[0] = addcmovc(aa - mm, yy, aa + yy);
    } else {
      limb_t ah = add_limb(kLimbs, a.n.limb_, y.n.limb_);
      maybe_minus_m(a.n.limb_, ah);
    }
  }

  // a -= y
  void sub(Elt& a, const Elt& y) const {
    if (kLimbs == 1) {
      a.n.limb_[0] = sub_sysdep(a.n.limb_[0], y.n.limb_[0], m_.limb_[0]);
    } else {
      limb_t ah = sub_limb(kLimbs, a.n.limb_, y.n.limb_);
      maybe_plus_m(a.n.limb_, ah);
    }
  }

  // x *= y, Montgomery
  void mul(Elt& x, const Elt& y) const {
    if (optimized_mul) {
      if (x == zero() || y == one()) {
        return;
      }
      if (y == zero() || x == one()) {
        x = y;
        return;
      }
    }
    mul0(x, y);
  }

  // x = -x
  void neg(Elt& x) const {
    Elt y(k_[0]);
    sub(y, x);
    x = y;
  }

  // x = 1/x
  void invert(Elt& x) const { x = invertf(x); }

  // functional interface
  Elt addf(Elt a, const Elt& y) const {
    add(a, y);
    return a;
  }
  Elt subf(Elt a, const Elt& y) const {
    sub(a, y);
    return a;
  }
  Elt mulf(Elt a, const Elt& y) const {
    mul(a, y);
    return a;
  }
  Elt negf(Elt a) const {
    neg(a);
    return a;
  }

  // This is the binary extended gcd algorithm, modified
  // to return the inverse of x.
  Elt invertf(Elt x) const {
    N a = from_montgomery(x);
    N b = m_;
    Elt u = one();
    Elt v = zero();
    while (a != /*zero*/ N{}) {
      if ((a.limb_[0] & 0x1u) == 0) {
        a.shiftr(1);
        byhalf(u);
      } else {
        if (a < b) {  // swap to maintain invariant
          std::swap(a, b);
          std::swap(u, v);
        }
        a.sub(b).shiftr(1);  // a = (a-b)/2
        sub(u, v);
        byhalf(u);
      }
    }
    return v;
  }

  // Reference implementation, unused.
  N from_montgomery_reference(const Elt& x) const {
    Elt r{N(1)};
    mul(r, x);
    return r.n;
  }

  // Optimized implementation of from_montgomery_reference(), exploiting
  // the fact that the multiplicand is Elt{N(1)}.
  N from_montgomery(const Elt& x) const {
    limb_t a[2 * kLimbs + 1];  // uninitialized
    mov(kLimbs, a, x.n.limb_);
    a[kLimbs] = zero_limb<limb_t>();
    for (size_t i = 0; i < kLimbs; ++i) {
      a[i + kLimbs + 1] = zero_limb<limb_t>();
      OPS::reduction_step(&a[i], mprime_, m_);
    }
    maybe_minus_m(a + kLimbs, a[2 * kLimbs]);
    N r;
    mov(kLimbs, r.limb_, a + kLimbs);
    return r;
  }

  Elt to_montgomery(const N& xn) const {
    Elt x{xn};
    mul(x, rsquare_);
    return x;
  }

  bool in_subfield(const Elt& e) const { return true; }

  // The of_scalar methods should only be used on trusted inputs known
  // at compile time to be valid field elements. As a result, they return
  // Elt directly instead of std::optional, and panic if the condition is not
  // satisfied. All untrusted input should be handled via the of_bytes method.
  Elt of_scalar(uint64_t a) const { return of_scalar_field(a); }

  Elt of_scalar_field(uint64_t a) const { return of_scalar_field(N(a)); }
  Elt of_scalar_field(const std::array<uint64_t, W64>& a) const {
    return of_scalar_field(N(a));
  }
  Elt of_scalar_field(const N& a) const {
    check(a < m_, "of_scalar must be less than m");
    return to_montgomery(a);
  }

  std::optional<Elt> of_bytes_field(const uint8_t ab[/* kBytes */]) const {
    N an = N::of_bytes(ab);
    if (an < m_) {
      return to_montgomery(an);
    } else {
      return std::nullopt;
    }
  }

  void to_bytes_field(uint8_t ab[/* kBytes */], const Elt& x) const {
    from_montgomery(x).to_bytes(ab);
  }

  std::optional<Elt> of_bytes_subfield(const uint8_t ab[/* kBytes */]) const {
    return of_bytes_field(ab);
  }

  void to_bytes_subfield(uint8_t ab[/* kBytes */], const Elt& x) const {
    to_bytes_field(ab, x);
  }

  const Elt& zero() const { return k_[0]; }
  const Elt& one() const { return k_[1]; }
  const Elt& two() const { return k_[2]; }
  const Elt& half() const { return half_; }
  const Elt& mone() const { return mone_; }

  Elt poly_evaluation_point(size_t i) const {
    check(i < kNPolyEvaluationPoints, "i < kNPolyEvaluationPoints");
    return poly_evaluation_points_[i];
  }

  // return (X[k] - X[k - i])^{-1}, were X[i] is the
  // i-th poly evalaluation point.
  Elt newton_denominator(size_t k, size_t i) const {
    check(k < kNPolyEvaluationPoints, "k < kNPolyEvaluationPoints");
    check(i <= k, "i <= k");
    check(k != (k - i), "k != (k - i)");
    return inv_small_scalars_[/* k - (k - i) = */ i];
  }

 private:
  void maybe_minus_m(limb_t a[kLimbs], limb_t ah) const {
    limb_t a1[kLimbs];
    mov(kLimbs, a1, negm_.limb_);
    limb_t ah1 = add_limb(kLimbs, a1, a);
    cmovne(kLimbs, a, ah, ah1, a1);
  }
  void maybe_plus_m(limb_t a[kLimbs], limb_t ah) const {
    limb_t a1[kLimbs];
    mov(kLimbs, a1, a);
    (void)add_limb(kLimbs, a1, m_.limb_);
    cmovnz(kLimbs, a, ah, a1);
  }

  void byhalf(Elt& a) const {
    if (a.n.shiftr(1) != 0) {
      // the lost bit is a raw 1, not one() in Montgomery form,
      // hence we must add a raw 1/2, not half().
      add(a, raw_half_);
    }
  }

  // unoptimized montgomery multiplication that does not
  // depend on the constants zero() and one() being defined.
  void mul0(Elt& x, const Elt& y) const {
    limb_t a[2 * kLimbs + 1];  // uninitialized
    mulstep<true>(a, x.n.limb_[0], y.n.limb_);
    for (size_t i = 1; i < kLimbs; ++i) {
      mulstep<false>(a + i, x.n.limb_[i], y.n.limb_);
    }
    maybe_minus_m(a + kLimbs, a[2 * kLimbs]);
    mov(kLimbs, x.n.limb_, a + kLimbs);
  }

  template <bool first>
  inline void mulstep(limb_t* a, limb_t x, const limb_t y[kLimbs]) const {
    if (kLimbs == 1) {
      // The general case (below) represents the (kLimbs+1)-word
      // product as L+(H<<bitsPerLimb), where in general L and H
      // overlap, requiring two additions.  For kLimbs==1, L and H do
      // not overlap, and we can interpret [L, H] as a single
      // double-precision number.
      a[2] = zero_limb<limb_t>();
      check(first, "mulstep template must be have first=true for 1 limb");
      mulhl(1, a, a + 1, x, y);
      OPS::reduction_step(a, mprime_, m_);
    } else {
      limb_t l[kLimbs], h[kLimbs];
      a[kLimbs + 1] = zero_limb<limb_t>();
      if (first) {
        a[kLimbs] = zero_limb<limb_t>();
        mulhl(kLimbs, a, h, x, y);
      } else {
        mulhl(kLimbs, l, h, x, y);
        accum(kLimbs + 1, a, kLimbs, l);
      }
      accum(kLimbs + 1, a + 1, kLimbs, h);
      OPS::reduction_step(a, mprime_, m_);
    }
  }

  // This method should only be used on static strings known at
  // compile time to be valid field elements.  We make it
  // private to prevent misuse.
  Elt of_charp(const char* s) const {
    Elt a(k_[0]);
    Elt base = of_scalar(10);
    if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
      s += 2;
      base = of_scalar(16);
    }

    for (; *s; s++) {
      Elt d = of_scalar(digit(*s));
      mul(a, base);
      add(a, d);
    }
    return a;
  }

  N m_;
  N negm_;
  Elt rsquare_;
  limb_t mprime_;
  Elt k_[3];  // small constants
  Elt half_;  // 1/2
  Elt raw_half_;
  Elt mone_;  // minus one
  Elt poly_evaluation_points_[kNPolyEvaluationPoints];
  Elt inv_small_scalars_[kNPolyEvaluationPoints];
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP_GENERIC_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_PLUCK_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_PLUCK_H_
#include <stddef.h>
#include <stdint.h>

#include <vector>

#include "algebra/interpolation.h"
#include "algebra/poly.h"
#include "circuits/logic/bit_plucker_constants.h"
#include "circuits/logic/polynomial.h"

namespace proofs {
// Special plucker that decodes into a pair (B, J) where B is one bit,
// and J is an array of NJ bits at most one of which can be set.
//
// B can assume one of two distinct values, and J can assume NJ+1
// distinct values.  Thus there are N = 2*(NJ+1) evaluation points.
// We encode J as the index IJ of which bit is set, or IJ=NJ if no bit
// is set.
template <class Logic, size_t NJ>
class CborPlucker {
 public:
  using Field = typename Logic::Field;
  using BitW = typename Logic::BitW;
  using EltW = typename Logic::EltW;
  using Elt = typename Field::Elt;
  static constexpr size_t kN = 2 * (NJ + 1);
  using PolyN = Poly<kN, Field>;
  using InterpolationN = Interpolation<kN, Field>;
  const Logic& l_;
  PolyN pluckerb_;
  std::vector<PolyN> pluckerj_;

  explicit CborPlucker(const Logic& l) : l_(l), pluckerj_(NJ) {
    const Field& F = l_.f_;  // shorthand
    // evaluation points
    PolyN X;
    for (size_t i = 0; i < kN; ++i) {
      X[i] = bit_plucker_point<Field, kN>()(i, F);
    }

    // encode B in the low-order bit
    PolyN Y;
    for (size_t i = 0; i < kN; ++i) {
      Y[i] = F.of_scalar(i & 1);
    }
    pluckerb_ = InterpolationN::monomial_of_lagrange(Y, X, F);

    // encode J in the high-order bits
    for (size_t j = 0; j < NJ; ++j) {
      for (size_t i = 0; i < kN; ++i) {
        Y[i] = F.of_scalar((i >> 1) == j);
      }
      pluckerj_[j] = InterpolationN::monomial_of_lagrange(Y, X, F);
    }
  }

  BitW pluckb(const EltW& e) const {
    const Logic& L = l_;  // shorthand
    const Polynomial<Logic> P(L);

    EltW v = P.eval(pluckerb_, e);
    L.assert_is_bit(v);
    return BitW(v, L.f_);
  }

  typename Logic::template bitvec<NJ> pluckj(const EltW& e) const {
    typename Logic::template bitvec<NJ> r;
    const Logic& L = l_;  // shorthand
    const Polynomial<Logic> P(L);

    for (size_t j = 0; j < NJ; ++j) {
      EltW v = P.eval(pluckerj_[j], e);
      L.assert_is_bit(v);
      r[j] = BitW(v, L.f_);
    }

    return r;
  }
};

template <class Field, size_t NJ>
struct cbor_plucker_point {
  using Elt = typename Field::Elt;
  static constexpr size_t kN = 2 * (NJ + 1);

  // packing of bits compatible with even_lagrange_basis():
  Elt operator()(bool b, size_t j, const Field& F) const {
    uint64_t bits = b + 2 * j;
    return bit_plucker_point<Field, kN>()(bits, F);
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_PLUCK_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA3_SHA3_CIRCUIT_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA3_SHA3_CIRCUIT_H_

// arithmetized sha3 using logic bitvectors
#include <stddef.h>

#include <cstdint>

#include "circuits/sha3/sha3_round_constants.h"

namespace proofs {
template <class LogicCircuit>
class Sha3Circuit {
  typedef typename LogicCircuit::template bitvec<64> v64;

  const LogicCircuit& lc_;

  v64 of_scalar(uint64_t x) const { return lc_.template vbit<64>(x); }

 public:
  explicit Sha3Circuit(const LogicCircuit& lc) : lc_(lc) {}

  void keccak_f_1600(v64 A[5][5]) {
    for (size_t round = 0; round < 24; ++round) {
      // FIPS 202 3.2.1, theta
      v64 C[5];
      for (size_t x = 0; x < 5; ++x) {
        auto a01 = lc_.vxor(&A[x][0], A[x][1]);
        auto a23 = lc_.vxor(&A[x][2], A[x][3]);
        C[x] = lc_.vxor(&a01, lc_.vxor(&a23, A[x][4]));
      }

      for (size_t x = 0; x < 5; ++x) {
        v64 D_x = lc_.vxor(&C[(x + 4) % 5], lc_.vrotl(C[(x + 1) % 5], 1));
        for (size_t y = 0; y < 5; ++y) {
          A[x][y] = lc_.vxor(&A[x][y], D_x);
        }
      }

      // FIPS 202 3.2.2, rho
      {
        size_t x = 1, y = 0;
        for (size_t t = 0; t < 24; ++t) {
          A[x][y] = lc_.vrotl(A[x][y], sha3_rotc[t]);
          size_t nx = y, ny = (2 * x + 3 * y) % 5;
          x = nx;
          y = ny;
        }
      }

      // FIPS 202 3.2.3, pi
      v64 A1[5][5];
      for (size_t x = 0; x < 5; ++x) {
        for (size_t y = 0; y < 5; ++y) {
          A1[x][y] = A[(x + 3 * y) % 5][x];
        }
      }

      // FIPS 202 3.2.4, chi
      for (size_t x = 0; x < 5; ++x) {
        for (size_t y = 0; y < 5; ++y) {
          A[x][y] = lc_.vxor(&A1[x][y], lc_.vand(&A1[(x + 2) % 5][y],
                                                 lc_.vnot(A1[(x + 1) % 5][y])));
        }
      }

      // FIPS 202 3.2.5, iota
      A[0][0] = lc_.vxor(&A[0][0], of_scalar(sha3_rc[round]));
    }
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA3_SHA3_CIRCUIT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_GF2K_GF2_128_H_
#define PRIVACY_PROOFS_ZK_LIB_GF2K_GF2_128_H_

#include <stdio.h>

#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <utility>

#include "gf2k/gf2poly.h"
#include "gf2k/sysdep.h"
#include "util/panic.h"

namespace proofs {

struct BinaryFieldTypeTag {};

template <size_t subfield_log_bits = 4>
class GF2_128 {
  // avoid writing static_cast<size_t>(1) all the time.
  static constexpr size_t k1 = 1;

 public:
  using TypeTag = BinaryFieldTypeTag;

  // Fast representation of the field element via the system-dependent
  // SIMD type.
  using N = gf2_128_elt_t;

  // "Slow" representation of the field element as array of
  // C++ integral types.
  using N1 = GF2Poly<2>;

  static constexpr size_t kNPolyEvaluationPoints = 6;
  static constexpr size_t kLogBits = 7;
  static constexpr size_t kBits = k1 << kLogBits;
  static constexpr size_t kBytes = kBits / 8u;

  static constexpr size_t kSubFieldLogBits = subfield_log_bits;
  static constexpr size_t kSubFieldBits = k1 << kSubFieldLogBits;
  static constexpr size_t kSubFieldBytes = kSubFieldBits / 8u;

  static_assert(kBits == 8u * kBytes);
  static_assert(kSubFieldBits == 8u * kSubFieldBytes);
  static constexpr bool kCharacteristicTwo = true;

  struct Elt {
    N n;

    Elt() : n{} {}
    explicit Elt(N n_) : n(n_) {}

    // Don't bother using SIMD instructions for comparisons,
    // otherwise we have to complicate the sysdep API surface.
    // Unpack into uint64[2] and compute manually.
    bool operator==(const Elt& y) const { return unpack() == y.unpack(); }
    bool operator!=(const Elt& y) const { return !operator==(y); }

    // Returns the coefficient of x^i in the polynomial.
    uint8_t operator[](size_t i) const {
      auto n1 = uint64x2_of_gf2_128(n);
      if (i < 64) {
        return static_cast<uint8_t>((n1[0] >> i) & 0x1);
      } else if (i < 128) {
        return static_cast<uint8_t>((n1[1] >> (i - 64)) & 0x1);
      } else {
        return 0;
      }
    }

    N1 unpack() const { return N1(uint64x2_of_gf2_128(n)); }
  };

  explicit GF2_128() {
    kone_ = of_scalar_field(0b1);
    kx_ = of_scalar_field(0b10);

    // x^{-1} = x^127 + x^6 + x + 1
    std::array<uint64_t, 2> invx = {
        (1ull << 6) | (1ull << 1) | (1ull << 0),
        (1ull << (127 - 64)),
    };
    kinvx_ = of_scalar_field(invx);

    Elt g = subfield_generator();

    // basis of the subfield = {1, g, g^2, ...}
    beta_[0] = one();
    for (size_t i = 1; i < kSubFieldBits; ++i) {
      beta_[i] = mulf(beta_[i - 1], g);
    }

    // Reduce the basis to row-echelon form
    beta_ref();

    // Evaluation points.  We use g^i for these as well
    poly_evaluation_points_[0] = zero();
    Elt gi = one();
    for (size_t i = 1; i < kNPolyEvaluationPoints; ++i) {
      poly_evaluation_points_[i] = gi;
      mul(gi, g);
    }

    for (size_t i = 1; i < kNPolyEvaluationPoints; i++) {
      for (size_t k = kNPolyEvaluationPoints; k-- > i;) {
        auto dx =
            subf(poly_evaluation_points_[k], poly_evaluation_points_[k - i]);
        check(dx != zero(), "dx != zero()");
        newton_denominators_[k][i] = invertf(dx);
      }
    }
  }

  GF2_128(const GF2_128&) = delete;
  GF2_128& operator=(const GF2_128&) = delete;

  // The bits of u are the coordinates with respect to the basis
  // beta_[] of the subfield.
  Elt of_scalar(uint64_t u) const {
    Elt t = zero();
    for (size_t k = 0; k < kSubFieldBits; ++k, u >>= 1) {
      if (u & 1) {
        add(t, beta_[k]);
      }
    }
    check(u == 0, "of_scalar(u), too many bits");
    return t;
  }

  Elt of_scalar_field(uint64_t n) const {
    std::array<uint64_t, 2> u = {n, 0};
    return of_scalar_field(u);
  }
  Elt of_scalar_field(const std::array<uint64_t, 2>& u) const {
    return Elt(gf2_128_of_uint64x2(u));
  }

  // The base_only flag is a placeholder that takes meaning when F is an
  // extension field.
  std::optional<Elt> of_bytes_field(const uint8_t ab[/* kBytes */],
                                    bool base_only = true) const {
    N1 an = N1::of_bytes(ab);
    return of_scalar_field(an.u64());
  }

  void to_bytes_field(uint8_t ab[/* kBytes */], const Elt& x) const {
    x.unpack().to_bytes(ab);
  }

  bool in_subfield(Elt e) const {
    auto eu = solve(e);
    return eu.first == N1{};
  }

  std::optional<Elt> of_bytes_subfield(
      const uint8_t ab[/* kSubFieldBytes */]) const {
    uint64_t u = 0;
    for (size_t i = kSubFieldBytes; i-- > 0;) {
      u <<= 8;
      u |= ab[i];
    }
    return of_scalar(u);
  }

  void to_bytes_subfield(uint8_t ab[/* kSubFieldBytes */], const Elt& x) const {
    auto eu = solve(x);
    check(eu.first == N1{}, "eu.first == N1{}");
    uint64_t u = eu.second;
    for (size_t i = 0; i < kSubFieldBytes; ++i) {
      ab[i] = u & 0xFFu;
      u >>= 8;
    }
  }

  // functional interface
  Elt addf(const Elt& x, const Elt& y) const {
    return Elt{gf2_128_add(x.n, y.n)};
  }
  Elt subf(const Elt& x, const Elt& y) const {
    return Elt{gf2_128_add(x.n, y.n)};
  }
  Elt mulf(const Elt& x, const Elt& y) const {
    return Elt{gf2_128_mul(x.n, y.n)};
  }
  Elt negf(const Elt& x) const { return x; }

  // two-operands interface
  void add(Elt& a, const Elt& y) const { a = addf(a, y); }
  void sub(Elt& a, const Elt& y) const { a = subf(a, y); }
  void mul(Elt& a, const Elt& y) const { a = mulf(a, y); }
  void neg(Elt& a) const { /* noop */ }
  void invert(Elt& a) const { a = invertf(a); }

  Elt zero() const { return Elt{}; }
  Elt one() const { return kone_; }
  Elt mone() const { return kone_; }
  Elt x() const { return kx_; }
  Elt invx() const { return kinvx_; }
  Elt beta(size_t i) const {
    check(i < kSubFieldBits, "i < kSubFieldBits");
    return beta_[i];
  }

  Elt poly_evaluation_point(size_t i) const {
    check(i < kNPolyEvaluationPoints, "i < kNPolyEvaluationPoints");
    return poly_evaluation_points_[i];
  }

  // return (X[k] - X[k - i])^{-1}, were X[i] is the
  // i-th poly evalaluation point.
  Elt newton_denominator(size_t k, size_t i) const {
    check(k < kNPolyEvaluationPoints, "k < kNPolyEvaluationPoints");
    check(i <= k, "i <= k");
    check(k != (k - i), "k != (k - i)");
    return newton_denominators_[k][i];
  }

  Elt invertf(Elt x) const {
    N1 a = x.unpack();
    // Let POLY be the generator of GF(2^128) as GF(2)[x]/(POLY(x)).
    // The Euclid algorithm would initialize B = POLY, but we cannot
    // store POLY in one N1.  Instead, we use the invariant that B is
    // always "odd" throughout the algorithm, and we represent B =
    // BM1OX * X + 1, or BM1OX = (B - 1) / X.  For B = POLY, BM1OX =
    // 1/X initially.
    N1 bm1ox = kinvx_.unpack();
    Elt u = one();
    Elt v = zero();
    while (a != N1(0)) {
      if (a.bit(0) == 0) {
        a.shiftr(1);
        byinvx(u);
      } else {
        // Now A is "odd".  Write A = AM1OX * X + 1.  This operation
        // be done in-place in the A variable, but we use another
        // name for clarity.
        N1 am1ox = a;
        am1ox.shiftr(1);

        // Normalize to the partial order degree(A) >= degree(B).
        // We use the stronger total order "<" which is consistent
        // with the partial order that we care about.
        if (am1ox < bm1ox) {
          std::swap(am1ox, bm1ox);
          std::swap(u, v);
        }
        am1ox.sub(bm1ox);
        sub(u, v);
        byinvx(u);
        a = am1ox;
      }
    }
    return v;
  }

 private:
  Elt kone_;
  Elt kx_;
  Elt kinvx_;                // x^{-1}
  Elt beta_[kSubFieldBits];  // basis of the subfield viewed as a
                             // vector space over GF(2)

  // LU decomposition of beta_, in unpacked format.  We store L^{-1}
  // instead of L, see comments in beta_ref()
  N1 u_[kSubFieldBits];
  uint64_t linv_[kSubFieldBits];

  // ldnz_[i] stores the column index of the leading nonzero in u_[i].
  // This array is in principle redundant, since one can always
  // reconstruct it from u_, but we cache it for efficiency.
  size_t ldnz_[kSubFieldBits];

  Elt poly_evaluation_points_[kNPolyEvaluationPoints];
  Elt newton_denominators_[kNPolyEvaluationPoints][kNPolyEvaluationPoints];

  void byinvx(Elt& u) const { mul(u, kinvx_); }

  Elt subfield_generator() {
    // Let k = kSubFieldLogBits and n = kLogBits.
    // Let x be the generator of Field.

    // The generator r of the subfield is then
    //    x^{(2^{2^n}-1)/(2^{2^k}-1)}

    // Compute r via the identity
    //   (2^{2^n}-1)/(2^{2^k}-1) =
    //      (2^{2^k}+1)*(2^{2^(k+1)}+1)*...*(2^{2^(n-1)}+1)
    Elt r(kx_);
    for (size_t i = kSubFieldLogBits; i < kLogBits; ++i) {
      // s <- r^{2^(2^i))
      Elt s(r);
      for (size_t j = 0; j < (1u << i); ++j) {
        mul(s, s);
      }
      // r <- r^{2^(2^i)+1)
      mul(r, s);
    }

    return r;
  }

  // beta_ref() is a just a variant of Gaussian elimination, but
  // because many such variants exist, we now explain the exact
  // mechanics of the algorithm.
  //
  // The problem that we need to solve is the inversion of
  // of_scalar(): given e = of_scalar(u), solve for u.  The constraint
  // we have is that e and u are arrays of bits, conveniently stored
  // in uint64_t, and ideally we want to perform parallel bitwise
  // operations, as opposed to extracting individual bits.
  //
  // Consider the following block matrix, or tableau:
  //
  //     [ B | -I ]
  //     [ ------ ]
  //     [ e | u  ]
  //
  // Here e and u are reinterpreted as row vectors of GF(2) elements;
  // I is the identity matrix; B is such that B[i] (the i-th row of b)
  // is beta(i) (the i-th element of the basis of the subfield), and
  // beta(i) is interpreted as a row vector of 128 GF(2) elements.
  // (The minus sign in -I is irrelevant over GF(2), but would be
  // necessary over other fields.)
  //
  // We now postulate that the only allowed operation on the tableau
  // is "axpy": add one row to another, which we can do efficiently
  // via bitwise xor.
  //
  // of_scalar(u) can be reinterpreted in terms axpy as follows.
  // Start with a tableau with e=0.  Reduce u to 0 via axpy
  // operations, e.g., for all i such that u[i] = 1, add row i to the
  // last row.  Because this is exactly what of_scalar() does, at the
  // end of the process we have e = of_scalar(u).  Because I is
  // full-rank, any sequence of axpy's that reduces u to 0 produces
  // the same e.
  //
  // We now want to invert the of_scalar() process.  We cannot apply
  // the axpy operations in of_scalar() in reverse order, because we
  // don't know u, and thus we don't know which operations
  // of_scalar(u) would apply.  However, because B is a basis, any
  // sequence of axpy operations that starts with u=0 and reduces e to
  // 0 reconstructs the same u.
  //
  // For lack of a better idea, we choose the following sequence of
  // axpy operations.  First reduce B to row-echelon form via axpy
  // operations on B, and then reduce e to zero via additional axpy
  // operations.  A matrix U is in row-echelon form if the following
  // condition holds: i' > i implies that U[i'][ldnz[i]] = 0, where
  // ldnz[i] is the column index of the leading nonzero in row i.
  //
  // Since B is constant, we choose to pre-compute the row-echelon
  // form of B in beta_ref(), and finish the process in solve() when e
  // is known, for multiple values of e.
  //
  // As it happens, reducing B to row-echelon transforms the -I
  // in the upper-right block to -L^{-1}, where B=LU is the LU
  // factorization of B.  We don't use this correspondence anywhere
  // in the code other than in the choice of the name Linv for the block.
  //
  void beta_ref() {
    for (size_t i = 0; i < kSubFieldBits; ++i) {
      // B in the tableau, becomes U at the end.
      u_[i] = beta_[i].unpack();

      // -I in the tableau, becomes -L^{-1} at the end.
      // Ignore the minus sign over GF(2).
      linv_[i] = (static_cast<uint64_t>(1) << i);
    }

    // Reduce B to row-echelon form.
    //
    // Invariant: B([0,RNK), [0,J)) is already in row-echelon form.
    // The loop body extends this property to J+1 and possibly RNK+1.
    size_t rnk = 0;
    for (size_t j = 0; rnk < kSubFieldBits && j < kBits; ++j) {
      // find pivot at row >= RNK in column J
      for (size_t i = rnk; i < kSubFieldBits; ++i) {
        if (u_[i].bit(j)) {
          std::swap(u_[rnk], u_[i]);
          std::swap(linv_[rnk], linv_[i]);
          goto have_pivot;
        }
      }
      // If we get here there is no pivot.  Keep the rank RNK the same
      // and proceed to the next column ++J
      continue;

    have_pivot:
      ldnz_[rnk] = j;

      // Pivot on [rnk][j].
      for (size_t i1 = rnk + 1; i1 < kSubFieldBits; ++i1) {
        if (u_[i1].bit(j)) {
          u_[i1].add(u_[rnk]);      // axpy on U
          linv_[i1] ^= linv_[rnk];  // axpy on Linv
        }
      }
      ++rnk;
    }

    // the basis is indeed a basis:
    check(rnk == kSubFieldBits, "rnk == kSubFieldBits");
  }

  std::pair<N1, uint64_t> solve(const Elt& e) const {
    uint64_t u = 0;
    N1 ue = e.unpack();
    for (size_t rnk = 0; rnk < kSubFieldBits; ++rnk) {
      size_t j = ldnz_[rnk];
      if (ue.bit(j)) {
        ue.add(u_[rnk]);
        u ^= linv_[rnk];
      }
    }

    return std::pair(ue, u);
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_GF2K_GF2_128_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ECDSA_VERIFY_WITNESS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ECDSA_VERIFY_WITNESS_H_

#include <cstddef>

#include "algebra/utility.h"
#include "arrays/dense.h"
#include "util/panic.h"

/*
Methods to help prepare witnesses for use in assertions about ecdsa.
*/
namespace proofs {

template <class EC, class ScalarField>
class VerifyWitness3 {
  using Field = typename EC::Field;
  using Elt = typename Field::Elt;
  using Nat = typename Field::N;
  using Point = typename EC::ECPoint;
  using Scalar = typename ScalarField::Elt;

 public:
  constexpr static size_t kBits = EC::kBits;
  const ScalarField& fn_;
  const EC& ec_;
  Elt rx_, ry_;
  Elt rx_inv_;
  Elt s_inv_;
  Elt pk_inv_;
  Elt pre_[8];
  Elt bi_[kBits];
  Elt int_x_[kBits];   /* Intermediate x,y elliptic curve points */
  Elt int_y_[kBits];   /* encountered during the scalar mult loop. */
  Elt int_z_[kBits];   /* z-coordinate of the intermediate points */

  VerifyWitness3(const ScalarField& Fn, const EC& ec) : fn_(Fn), ec_(ec) {}

  void fill_witness(DenseFiller<Field>& filler) const {
    filler.push_back(rx_);
    filler.push_back(ry_);
    filler.push_back(rx_inv_);
    filler.push_back(s_inv_);
    filler.push_back(pk_inv_);
    for (size_t i = 0; i < 8; ++i) {
      filler.push_back(pre_[i]);
    }
    for (size_t i = 0; i < kBits; ++i) {
      filler.push_back(bi_[i]);
      if (i < kBits - 1) {
        filler.push_back(int_x_[i]);
        filler.push_back(int_y_[i]);
        filler.push_back(int_z_[i]);
      }
    }
  }

  // Produces witnesses to support the verification of the equation
  //     id = g*e + pk*r + (rx,ry)*-s
  // Note that the same rx is interpreted in scalar field as r.
  bool compute_witness(const Elt pkX, const Elt pkY, const Nat e, const Nat r,
                       const Nat s) {
    const Field& F = ec_.f_;
    const Scalar _s = fn_.invertf(fn_.to_montgomery(s));
    const Scalar tms = fn_.negf(fn_.to_montgomery(s));

    // Because Fp does not have a sqrt method, compute ry via the
    // elliptic curve point g*(e/s) + pk*(r/s).
    auto te_s = fn_.mulf(fn_.to_montgomery(e), _s);
    auto tr_s = fn_.mulf(fn_.to_montgomery(r), _s);
    const Nat nes = fn_.from_montgomery(te_s);
    const Nat nrs = fn_.from_montgomery(tr_s);
    Point bases[] = {ec_.generator(), Point(pkX, pkY, F.one())};
    Nat scalars[] = {nes, nrs};
    auto pr = ec_.scalar_multf(2, bases, scalars);
    ec_.normalize(pr);

    rx_ = F.to_montgomery(r);
    ry_ = pr.y;

    // In the case of a malicious input with rx=0 or s=0, the proof will fail.
    if (rx_ != F.zero()) {
      rx_inv_ = F.invertf(rx_);
      check(F.mulf(rx_, rx_inv_) == F.one(), "bad inv");
    }

    s_inv_ = F.to_montgomery(fn_.from_montgomery(tms));
    if (s_inv_ != F.zero()) {
      F.invert(s_inv_);
    }

    if (pkX != F.zero()) {
      pk_inv_ = F.invertf(pkX);
    }

    const Nat nms = fn_.from_montgomery(tms);   /* -s */

    // Produce the table of pre-computed g,r,pk sums.
    const Elt one = F.one(), gX = ec_.gx_, gY = ec_.gy_;
    const Elt lh[] = {gX, gY, gX, gY, pkX, pkY};
    const Elt rh[] = {pkX, pkY, rx_, ry_, rx_, ry_};
    Elt zi;
    for (size_t i = 0; i < 3; ++i) {
      ec_.addE(pre_[2 * i], pre_[2 * i + 1], zi,
               lh[2 * i], lh[2 * i + 1], one,
               rh[2 * i], rh[2 * i + 1], one);

      // This invert cannot fail because both the generator and pk are
      // trusted inputs, so the above addition is not the identity.
      // In the case that it is, the proof will fail (and it should, since
      // the system is unsound with sk=-1).
      if (zi != F.zero()) {
        F.invert(zi);
      }
      F.mul(pre_[2 * i], zi);
      F.mul(pre_[2 * i + 1], zi);
    }
    // rgpk
    ec_.addE(pre_[6], pre_[7], zi, pre_[2], pre_[3], one, pkX, pkY, one);
    if (zi != F.zero()) {
      F.invert(zi);
    }
    F.mul(pre_[6], zi);
    F.mul(pre_[7], zi);

    Elt aX = F.zero(), aY = one, aZ = F.zero();

    // Compute b[], and intermediate points, encode b as:
    //  1:g  2:pk  3: gpk  4: r  5: r+g  6: r+pk  7:g+r+pk
    // Elt int_z[kBits];
    size_t b[kBits];
    // bool early_zero = false;   /* indicates if any intermediate z is zero */
    for (size_t i = 0; i < kBits; ++i) {
      b[i] = e.bit(kBits - i - 1) + 2 * r.bit(kBits - i - 1) +
             4 * nms.bit(kBits - i - 1);

      // Manually compute standard (-n...n representation).
      bi_[i] = F.subf(F.of_scalar(2 * b[i]), F.of_scalar(7));

      if (i > 0) {
        ec_.doubleE(aX, aY, aZ, aX, aY, aZ);
      }
      switch (b[i]) {
        case 0:
          ec_.addE(aX, aY, aZ, aX, aY, aZ, F.zero(), F.one(), F.zero());
          break;
        case 1:
          ec_.addE(aX, aY, aZ, aX, aY, aZ, gX, gY, one);
          break;
        case 2:
          ec_.addE(aX, aY, aZ, aX, aY, aZ, pkX, pkY, one);
          break;
        case 3:
          ec_.addE(aX, aY, aZ, aX, aY, aZ, pre_[0], pre_[1], one);
          break;
        case 4:
          ec_.addE(aX, aY, aZ, aX, aY, aZ, rx_, ry_, one);
          break;
        case 5:
          ec_.addE(aX, aY, aZ, aX, aY, aZ, pre_[2], pre_[3], one);
          break;
        case 6:
          ec_.addE(aX, aY, aZ, aX, aY, aZ, pre_[4], pre_[5], one);
          break;
        case 7:
          ec_.addE(aX, aY, aZ, aX, aY, aZ, pre_[6], pre_[7], one);
          break;
      }

      int_x_[i] = aX;
      int_y_[i] = aY;
      int_z_[i] = aZ;
    }

    if (aX != F.zero()) {
      return false;
    }
    if (aZ != F.zero()) {
      return false;
    }

    return true;
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ECDSA_VERIFY_WITNESS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_ADDER_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_ADDER_H_

#include <stddef.h>

#include <cstdint>
#include <vector>

// Circuit element that maps bitvec<N> V to a field element E
// in such a way that:
//
// 1) addition can be performed efficiently, e.g. as field
//    addition or field multiplication
// 2) given and E that is the sum of K bitvecs, a simple circuit
//    asserts that E = A mod 2^N
namespace proofs {

template <class Logic, size_t N, bool kCharacteristicTwo>
class BitAdderAux;

// Use the additive group in fields with large characteristic
template <class Logic, size_t N>
class BitAdderAux<Logic, N, /*kCharacteristicTwo=*/false> {
 public:
  using Field = typename Logic::Field;
  using BitW = typename Logic::BitW;
  using EltW = typename Logic::EltW;
  using Elt = typename Field::Elt;
  using BV = typename Logic::template bitvec<N>;
  const Logic& l_;

  explicit BitAdderAux(const Logic& l) : l_(l) {}

  EltW as_field_element(const BV& v) const {
    const Logic& L = l_;  // shorthand
    constexpr uint64_t uno = 1;
    EltW r = L.konst(L.zero());
    for (size_t i = 0; i < N; ++i) {
      auto vi = L.eval(v[i]);
      r = L.axpy(&r, L.elt(uno << i), vi);
    }
    return r;
  }

  EltW add(const EltW* a, const EltW& b) const { return l_.add(a, b); }
  EltW add(const BV& a, const BV& b) const {
    auto a_fe = as_field_element(a);
    auto b_fe = as_field_element(b);
    return add(&a_fe, b_fe);
  }
  EltW add(const std::vector<BV>& a) const {
    return l_.add(0, a.size(),
                  [&](size_t i) { return as_field_element(a[i]); });
  }

  // assert that B = A + i*2^N for 0 <= i < k
  void assert_eqmod(const BV& a, const EltW& b, size_t k) const {
    const Logic& L = l_;  // shorthand
    constexpr uint64_t uno = 1;
    EltW z = L.sub(&b, as_field_element(a));
    EltW zz = L.mul(0, k, [&](size_t i) {
      return L.sub(&z, L.konst((uno << N) * i));
    });
    L.assert0(zz);
  }
};

// Use the multiplicative group in GF(2^k)
template <class Logic, size_t N>
class BitAdderAux<Logic, N, /*kCharacteristicTwo=*/true> {
 public:
  using Field = typename Logic::Field;
  using BitW = typename Logic::BitW;
  using EltW = typename Logic::EltW;
  using Elt = typename Field::Elt;
  using BV = typename Logic::template bitvec<N>;
  const Logic& l_;

  explicit BitAdderAux(const Logic& l) : l_(l) {
    const Logic& L = l_;     // shorthand
    const Field& F = l_.f_;  // shorthand

    // assume that X is a root of unity of order large enough.
    Elt alpha = F.x();

    for (size_t i = 0; i < N; ++i) {
      alpha_2_i_[i] = alpha;
      alpha = L.mulf(alpha, alpha);
    }
    alpha_2_N_ = alpha;
  }

  EltW as_field_element(const BV& v) const {
    const Logic& L = l_;  // shorthand
    return L.mul(0, N, [&](size_t i) {
      auto a2i = L.konst(alpha_2_i_[i]);
      return L.mux(&v[i], &a2i, L.konst(L.one()));
    });
  }

  EltW add(const EltW* a, const EltW& b) const { return l_.mul(a, b); }
  EltW add(const BV& a, const BV& b) const {
    auto a_fe = as_field_element(a);
    auto b_fe = as_field_element(b);
    return add(&a_fe, b_fe);
  }
  EltW add(const std::vector<BV>& a) const {
    return l_.mul(0, a.size(),
                  [&](size_t i) { return as_field_element(a[i]); });
  }

  // assert that B = A + alpha^(i*2^N) for 0 <= i < k
  void assert_eqmod(const BV& a, const EltW& b, size_t k) const {
    const Logic& L = l_;     // shorthand
    const Field& F = l_.f_;  // shorthand

    std::vector<Elt> p(k);
    p[0] = F.one();
    for (size_t i = 1; i < k; ++i) {
      p[i] = F.mulf(alpha_2_N_, p[i - 1]);
    }
    EltW aa = as_field_element(a);
    EltW prod = L.mul(0, k, [&](size_t i) {
      auto pi = L.konst(p[i]);
      return L.sub(&b, L.mul(&pi, aa));
    });
    L.assert0(prod);
  }

 private:
  Elt alpha_2_i_[N + 1];
  Elt alpha_2_N_;
};

template <class Logic, size_t N>
using BitAdder = BitAdderAux<Logic, N, Logic::Field::kCharacteristicTwo>;
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_ADDER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CBOR_HOST_DECODER_H_
#define PRIVACY_PROOFS_ZK_LIB_CBOR_HOST_DECODER_H_

#include <stddef.h>
#include <string.h>

#include <cstdint>
#include <vector>

#include "util/panic.h"

namespace proofs {

enum CborTag { UNSIGNED, NEGATIVE, BYTES, TEXT, ARRAY, MAP, TAG, PRIMITIVE };
enum CborPrimitive { FALSE, TRUE, CNULL };

// CBOR decoder for a subset of CBOR used in MDOC.
//
// The main advantage of this decoder is that it keeps
// offsets into the input, which is useful because we need to
// generate circuits that depend on input offsets.
//
// The other security advantage is the smaller codebase, versus
// relying on an imported CBOR parser that handles a larger subset of CBOR
// that may introduce issues.
//
// The decode function is used to process an untrusted array of bytes.
// The method returns false if the input is not processed exactly per the
// MDOC spec with only attributes in the org.iso.18013.5.1 namespace.
// The resulting CborDoc object is static, and it is assumed that neither the
// input doc, nor the tree structure changes. All of the lookup and index
// methods return const pointers to attempt to maintain this property.
class CborDoc {
 public:
  size_t header_pos_;
  enum CborTag t_;

  // A union is used to store the attributes for either singleton objects (i.e.,
  // UNSIGNED, NEGATIVE, PRIMITIVE), the start position and len of TEXT and
  // BYTES array, and the children information for ARRAY or MAP objects.
  // len of strings and byte arrays
  union U {
    uint64_t u64;         /* UNSIGNED */
    int64_t i64;          /* NEGATIVE */
    enum CborPrimitive p; /* PRIMITIVE */

    // BYTES + TEXT, represented as offset in input + length
    struct {
      size_t pos;
      size_t len;
    } string;

    // arrays, maps, and tags: an array of children nodes.
    struct {
      // The original count in the source document.  For tags,
      // the tag itself.
      size_t n;

      // The actual number of children (e.g. 2*n for maps).
      size_t nchildren;
    } items;
  } u_;

  // This field only applies to ARRAY, MAP nodes, but it has been moved
  // out of the union to avoid including components with non-default
  // constructors. It holds the children objects of an array or map. For a map,
  // even positions are the keys, and the odd positions are the values.
  std::vector<CborDoc> children_;

  // Parse a byte sequence into a CborDoc structure.
  //
  // Caller passes in the input sequence, the length of the
  // input, and pos and offset values. The offset value handles the case when
  // the input sequence is a sub-sequence of another string, as it is in
  // the MDOC and MSO parsing.
  //
  // This function can handle adversarial inputs, and returns false when the
  // input cannot be parsed.
  bool decode(const uint8_t in[], size_t len, size_t &pos, size_t offset) {
    /* invariant: pos is always compared with len before it is referenced. */
    header_pos_ = pos + offset;

    if (pos >= len) {
      return false;
    }
    uint8_t b = in[pos++];

    size_t type = (b >> 5) & 0x7u;
    size_t count0 = b & 0x1Fu;

    // variable-length count
    size_t count = 0;
    if (count0 < 24) {
      count = count0;
    } else if (count0 == 24) {
      if (pos >= len) {
        return false;
      }
      count = in[pos++];
    } else if (count0 == 25) {
      if (pos + 1 >= len) {
        return false;
      }
      count = in[pos] * 256 + in[pos + 1];
      pos += 2;
    } else if (count0 == 26) {
      if (pos + 3 >= len) {
        return false;
      }
      for (size_t i = 0; i < 4; ++i) {
        count *= 256;
        count += in[pos++];
      }
    } else {
      return false;
    }

    switch (type) { /* type \in [0,7] by construction */
      case 0:
        t_ = UNSIGNED;
        u_.u64 = count;
        break;
      case 1:
        t_ = NEGATIVE;
        u_.i64 = -(int64_t)count;
        break;

      case 2: /* BYTES */
      case 3: /* TEXT */
        if (pos + count > len) {
          return false;
        }
        t_ = (type == 2) ? BYTES : TEXT;
        u_.string.pos = pos;
        u_.string.len = count;
        pos += count;
        break;

      case 4: /* ARRAY */
        if (pos + count > len) {
          return false;
        }
        return decode_items(ARRAY, count, count, in, len, pos, offset);

      case 5: /* MAP, (key,val) pairs are stored as 2*children */
        if (pos + 2 * count > len) {
          return false;
        }
        return decode_items(MAP, 2 * count, count, in, len, pos, offset);

      case 6: /* TAG */
        // Special cases for TAG
        if (count == 1004) {  // date in the form YYYY-MM-DD
          if (pos + 1 + 10 > len) {  // 0xDA for str length + 10 characters
            return false;
          }
        }
        return decode_items(TAG, 1, count, in, len, pos, offset);

      case 7: /* PRIMITIVE */
        t_ = PRIMITIVE;
        switch (count) {
          case 20:
            u_.p = FALSE;
            break;
          case 21:
            u_.p = TRUE;
            break;
          case 22:
            u_.p = CNULL;
            break;
          default:
            return false;
        }
        break;
    }

    return true;
  }

  // Lookup a child node in an array. Returns null if the query is invalid.
  const CborDoc *index(size_t index) const {
    if (t_ == ARRAY && index < u_.items.nchildren) {
      return &children_[index];
    }
    return nullptr;
  }

  // Lookup a key in a map of type {bytes->elements}.
  // Returns null if the query is invalid.
  // The key is given as bytes with a length.
  // ndx is set to the child index of the located key.
  // The return pointer references the key, and the next object refers to
  // the value and is guaranteed to exist.
  const CborDoc *lookup(const uint8_t *const in, size_t len,
                        const uint8_t bytes[/*len*/], size_t &ndx) const {
    if (t_ == MAP) {
      for (size_t i = 0; i < u_.items.n; ++i) {
        if (children_[2 * i].eq(in, len, bytes)) {
          ndx = i;
          return &children_[2 * i];
        }
      }
    }
    return nullptr;
  }

  // Lookup a key in a map of type {unsigned->object}.
  // Returns null if the query is invalid.
  const CborDoc *lookup_unsigned(uint64_t k, size_t &ndx) const {
    if (t_ == MAP) {
      for (size_t i = 0; i < u_.items.n; ++i) {
        const CborDoc *key = &children_[2 * i];
        if (key->t_ == UNSIGNED && key->u_.u64 == k) {
          ndx = i;
          return key;
        }
      }
    }
    return nullptr;
  }

  // Lookup a key in a map of type {negative->object}.
  // Returns null if the query is invalid.
  const CborDoc *lookup_negative(int64_t k, size_t &ndx) const {
    if (t_ == MAP) {
      for (size_t i = 0; i < u_.items.n; ++i) {
        const CborDoc *key = &children_[2 * i];
        if (key->t_ == NEGATIVE && key->u_.i64 == k) {
          ndx = i;
          return key;
        }
      }
    }
    return nullptr;
  }

  // Returns the index of the item with respect to the document bytes.
  size_t position() const {
    switch (t_) {
      case UNSIGNED:
        return header_pos_;
      case BYTES:
      case TEXT:
        return u_.string.pos;
      case TAG:
        return children_[0].u_.string.pos;
      case PRIMITIVE:
        return header_pos_;
      default:
        check(false, "valueIndex called on non-value type");
    }
    return 0;
  }

  // Returns the length of the item's value in bytes.
  // According to ISO 18013-5 7.2.1, the mDL data elements shall be encoded
  // as tstr, uint, bstr, bool, or tdate, so this function only handles those
  // cases.
  size_t length() const {
    switch (t_) {
      case UNSIGNED:
        if (u_.u64 < 24) {
          return 1;
        } else if (u_.u64 < 256) {
          return 2;
        } else if (u_.u64 < 65536) {
          return 3;
        }
        return 5;
      case BYTES:
      case TEXT:
        return u_.string.len;
      case TAG:
        return children_[0].u_.string.len;  //  full-date #6.1004(tstr) format
      case PRIMITIVE:
        return 1;
      default:
        check(false, "valueLength called on non-value type");
    }
    return 0;
  }

 private:
  // Decodes a sequence of children nodes.
  bool decode_items(CborTag t, size_t nchildren, size_t items_n,
                    const uint8_t in[], size_t len, size_t &pos,
                    size_t offset) {
    t_ = t;
    u_.items.n = items_n;
    u_.items.nchildren = nchildren;
    children_.resize(nchildren);
    for (size_t i = 0; i < nchildren; ++i) {
      if (!children_[i].decode(in, len, pos, offset)) return false;
    }
    return true;
  }

  // Compares a text node to a given string of bytes.
  bool eq(const uint8_t *const in, size_t len,
          const uint8_t bytes[/*len*/]) const {
    return t_ == TEXT && u_.string.len == len &&
           memcmp(bytes, &in[u_.string.pos], len) == 0;
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CBOR_HOST_DECODER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_NAT_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_NAT_H_

#include <array>
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <optional>

#include "algebra/limb.h"
#include "algebra/static_string.h"
#include "algebra/sysdep.h"
#include "util/panic.h"

namespace proofs {

// return a^-1 mod 2^L where L is the number of bits in limb_t
template <class limb_t>
static limb_t inv_mod_b(limb_t a) {
  // Let v=1-a.  We have 1/a=1/(1-v)=1+v+v^2+..., or
  // 1/a=(1+v)(1+v^2)(1+v^4)... At some point v^(2^k) becomes 0 mod
  // 2^L because v is even.

  // A more complicated variant of this idea appears in Dumas,
  // J.G. "On Newton–Raphson Iteration for Multiplicative Inverses
  // Modulo Prime Powers", Algorithm 3, where they use v'=a-1
  // instead of v=1-a, and so the first term needs to be handled
  // separately as 2-a instead of 1+v, breaking the uniformity of
  // the algorithm.  The sign difference disappears after the first
  // squaring.
  check((a & 1) != 0, "even A in inv_mod_b()");

  limb_t v = 1u - a;
  limb_t u = 1u;
  while (v != 0) {
    u *= (1u + v);
    v *= v;
  }
  return u;
}

// This function should only be called on static input known at compile time.
unsigned digit(char c);

template <size_t W64>
class Nat : public Limb<W64> {
 public:
  using Super = Limb<W64>;
  using T = Nat<W64>;
  using limb_t = typename Super::limb_t;
  using Super::kLimbs;
  using Super::kU64;
  using Super::limb_;

  // Maximum length for an untrusted string, 2^64 ~ 20 decimal digits.
  static constexpr size_t kMaxStringLen = 20 * W64 + 1;

  Nat() = default;  // uninitialized
  explicit Nat(uint64_t x) : Super(x) {}

  explicit Nat(const std::array<uint64_t, kU64>& a) : Super(a) {}

  // Pre-condition: the caller of this function must check that the string
  // s is either a valid base-10 or base-16 representation of a natural number
  // that does not overflow the representation.
  // In our current implementation, this method is only used on static strings.
  explicit Nat(const StaticString& ss) : Super(0) {
    limb_t base = 10u;
    const char* s = ss.as_pointer;
    if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
      s += 2;
      base = 16u;
    }
    for (; *s; s++) {
      T d(digit(*s));
      bool ok = muls(limb_, base);
      check(ok, "overflow in nat(const char *s)");
      limb_t ah = add_limb(kLimbs, limb_, d.limb_);
      check(ah == 0, "overflow in nat(const char *s)");
    }
  }

  template <size_t LEN>
  explicit Nat(const char (&p)[LEN]) : Nat(StaticString(p)) {}

  // Interpret A[] as a little-endian nat
  static T of_bytes(const uint8_t a[/* kBytes */]) {
    T r;
    for (size_t i = 0; i < kLimbs; ++i) {
      a = Super::of_bytes(&r.limb_[i], a);
    }
    return r;
  }

  static std::optional<unsigned> safe_digit(char c, limb_t base) {
    c = tolower(c);
    if (c >= '0' && c <= '9') {
      return c - '0';
    } else if (base == 16u && c >= 'a' && c <= 'f') {
      return c - 'a' + 10;
    }
    return std::nullopt;
  }

  static std::optional<T> of_untrusted_string(const char* s) {
    T r(0);
    limb_t base = 10u;
    if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
      s += 2;
      base = 16u;
    }
    const char* p = s;
    for (size_t len = 0; len < kMaxStringLen && *p; ++len, ++p) {
      auto d = safe_digit(*p, base);
      if (!d.has_value()) {
        return std::nullopt;
      }
      T td(d.value());
      if (!muls(r.limb_, base)) {
        return std::nullopt;
      }
      limb_t ah = add_limb(kLimbs, r.limb_, td.limb_);
      if (ah != 0) {
        return std::nullopt;
      }
    }
    // If the loop terminates due to the length limit, then the string is not
    // a valid base-10 or base-16 representation of a natural number.
    if (*p) {
      return std::nullopt;
    }
    return r;
  }

  bool operator<(const T& y) const {
    T b = *this;
    limb_t bh = sub_limb(kLimbs, b.limb_, y.limb_);
    return (bh != 0);
  }

  T& add(const T& y) {
    (void)add_limb(kLimbs, limb_, y.limb_);
    return *this;
  }
  T& sub(const T& y) {
    (void)sub_limb(kLimbs, limb_, y.limb_);
    return *this;
  }

 private:
  // b *= a, returns false if overflow occurred.
  static bool muls(limb_t b[kLimbs], limb_t a) {
    limb_t h[kLimbs];
    mulhl(kLimbs, b, h, a, b);
    limb_t bh = addh(kLimbs, b, h);
    return bh == 0;
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_NAT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_SUMCHECK_QUAD_H_
#define PRIVACY_PROOFS_ZK_LIB_SUMCHECK_QUAD_H_

#include <stddef.h>

#include <algorithm>
#include <cstdint>
#include <memory>
#include <vector>

#include "algebra/compare.h"
#include "algebra/poly.h"
#include "arrays/affine.h"
#include "arrays/eqs.h"
#include "util/ceildiv.h"
#include "util/panic.h"
#define DEFINE_STRONG_INT_TYPE(a, b) using a = b

// ------------------------------------------------------------
// Special-purpose sparse array for use with sumcheck
namespace proofs {
template <class Field>
class Quad {
  using Elt = typename Field::Elt;
  using T2 = Poly<2, Field>;

 public:
  // To save space when representing large circuits, quad_corner_t
  // is defined as uint32_t.  (Note that Elt probably imposes uint64_t
  // alignment, so struct corner has holes.)
  //
  // To make the narrowing explicit, define corner_t as a
  // Google-specific strong int.  Outside of Google, replace
  // this definition with a typedef.
  DEFINE_STRONG_INT_TYPE(quad_corner_t, uint32_t);

  struct corner {
    quad_corner_t g;     // "gate" variable
    quad_corner_t h[2];  // two "hand" variables
    Elt v;

    bool operator==(const corner& y) const {
      return g == y.g &&
             morton::eq(size_t(h[0]), size_t(h[1]), size_t(y.h[0]),
                        size_t(y.h[1])) &&
             v == y.v;
    }

    bool eqndx(const corner& y) const {
      return (g == y.g && h[0] == y.h[0] && h[1] == y.h[1]);
    }

    void canonicalize() {
      quad_corner_t h0 = h[0], h1 = h[1];
      h[0] = std::min<quad_corner_t>(h0, h1);
      h[1] = std::max<quad_corner_t>(h0, h1);
    }

    static bool compare(const corner& x, const corner& y, const Field& F) {
      if (morton::lt(size_t(x.h[0]), size_t(x.h[1]), size_t(y.h[0]),
                     size_t(y.h[1]))) {
        return true;
      } else if (morton::eq(size_t(x.h[0]), size_t(x.h[1]), size_t(y.h[0]),
                            size_t(y.h[1]))) {
        if (x.g < y.g) return true;
        if (x.g > y.g) return false;
        return elt_less_than(x.v, y.v, F);
      } else {
        return false;
      }
    }
  };

  using index_t = size_t;
  index_t n_;
  std::vector<corner> c_;

  bool operator==(const Quad& y) const {
    return n_ == y.n_ &&
           std::equal(c_.begin(), c_.end(), y.c_.begin(), y.c_.end());
  }

  explicit Quad(index_t n) : n_(n), c_(n) {}

  // no copies, but see clone() below
  Quad(const Quad& y) = delete;
  Quad(const Quad&& y) = delete;
  Quad operator=(const Quad& y) = delete;

  std::unique_ptr<Quad> clone() const {
    auto s = std::make_unique<Quad>(n_);
    for (index_t i = 0; i < n_; ++i) {
      s->c_[i] = c_[i];
    }
    return s;
  }

  void bind_h(const Elt& r, size_t hand, const Field& F) {
    index_t rd = 0, wr = 0;
    while (rd < n_) {
      corner cc;
      cc.g = quad_corner_t(0);
      cc.h[hand] = c_[rd].h[hand] >> 1;
      cc.h[1 - hand] = c_[rd].h[1 - hand];

      size_t rd1 = rd + 1;
      if (rd1 < n_ &&                                         //
          c_[rd].h[1 - hand] == c_[rd1].h[1 - hand] &&        //
          (c_[rd].h[hand] >> 1) == (c_[rd1].h[hand] >> 1) &&  //
          c_[rd1].h[hand] == c_[rd].h[hand] + quad_corner_t(1)) {
        // we have two corners.
        cc.v = affine_interpolation(r, c_[rd].v, c_[rd1].v, F);
        rd += 2;
      } else {
        // we have one corner and the other one is zero.
        if ((c_[rd].h[hand] & quad_corner_t(1)) == quad_corner_t(0)) {
          cc.v = affine_interpolation_nz_z(r, c_[rd].v, F);
        } else {
          cc.v = affine_interpolation_z_nz(r, c_[rd].v, F);
        }
        rd = rd1;
      }

      c_[wr++] = cc;
    }

    // shrink the array
    n_ = wr;
  }

  // Set zero coefficients to BETA, then bind to both
  // G0 and G1 and take the linear combination bind(G0) + alpha*bind(G1)
  void bind_g(size_t logv, const Elt* G0, const Elt* G1, const Elt& alpha,
              const Elt& beta, const Field& F) {
    size_t nv = size_t(1) << logv;
    auto dot = Eqs<Field>::raw_eq2(logv, nv, G0, G1, alpha, F);
    for (index_t i = 0; i < n_; ++i) {
      if (c_[i].v == F.zero()) {
        c_[i].v = beta;
      }
      F.mul(c_[i].v, dot[corner_t(c_[i].g)]);
      c_[i].g = quad_corner_t(0);
    }

    // coalesce any duplicates that we may have created
    coalesce(F);
  }

  // Optimized combined bind_g + bind_h, nondestructive
  Elt bind_gh_all(
      // G bindings
      size_t logv, const Elt G0[/*logv*/], const Elt G1[/*logv*/],
      const Elt& alpha, const Elt& beta,
      // H bindings
      size_t logw, const Elt H0[/*logw*/], const Elt H1[/*logw*/],
      // field
      const Field& F) const {
    size_t nv = size_t(1) << logv;
    auto eqg = Eqs<Field>::raw_eq2(logv, nv, G0, G1, alpha, F);

    size_t nw = size_t(1) << logw;
    Eqs<Field> eqh0(logw, nw, H0, F);
    Eqs<Field> eqh1(logw, nw, H1, F);

    Elt s{};

    for (index_t i = 0; i < n_; ++i) {
      Elt q(c_[i].v);
      if (q == F.zero()) {
        q = beta;
      }
      F.mul(q, eqg[corner_t(c_[i].g)]);
      F.mul(q, eqh0.at(corner_t(c_[i].h[0])));
      F.mul(q, eqh1.at(corner_t(c_[i].h[1])));
      F.add(s, q);
    }
    return s;
  }

  Elt scalar() {
    check(n_ == 1, "n_ == 1");
    check(c_[0].g == quad_corner_t(0), "c_[0].g == 0");
    check(c_[0].h[0] == quad_corner_t(0), "c_[0].h[0] == 0");
    check(c_[0].h[1] == quad_corner_t(0), "c_[0].h[1] == 0");
    return c_[0].v;
  }

  void canonicalize(const Field& F) {
    for (index_t i = 0; i < n_; ++i) {
      c_[i].canonicalize();
    }
    std::sort(c_.begin(), c_.end(), [&F](const corner& x, const corner& y) {
      return corner::compare(x, y, F);
    });
    coalesce(F);
  }

 private:
  void coalesce(const Field& F) {
    // Coalesce duplicates.
    // The (rd,wr)=(0,0) iteration executes the else{} branch and
    // continues with (1,1), so we start at (1,1) and avoid the
    // special case for wr-1 at wr=0.
    index_t wr = 1;
    for (index_t rd = 1; rd < n_; ++rd) {
      if (c_[rd].eqndx(c_[wr - 1])) {
        F.add(c_[wr - 1].v, c_[rd].v);
      } else {
        c_[wr] = c_[rd];
        wr++;
      }
    }
    n_ = wr;
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_SUMCHECK_QUAD_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_GF2K_LCH14_H_
#define PRIVACY_PROOFS_ZK_LIB_GF2K_LCH14_H_

#include <stdio.h>

#include <vector>

#include "util/panic.h"

// The algorithm from [LCH14] following [DP24, Algorithm 2]
//
// [LCH14] Sian-Jheng Lin, Wei-Ho Chung, and Yunghsiang S. Han: Novel
// Polynomial Basis and Its Application to Reed-Solomon Erasure Codes,
// https://arxiv.org/pdf/1404.3458

// [DP24] Benjamin E. Diamond and Jim Posen, Polylogarithmic Proofs
// for Multilinears over Binary Towers, https://eprint.iacr.org/2024/504

namespace proofs {

template <class Field>
class LCH14 {
  using Elt = typename Field::Elt;

  // only works in binary fields
  static_assert(Field::kCharacteristicTwo);

 public:
  static constexpr size_t kSubFieldBits = Field::kSubFieldBits;

  explicit LCH14(const Field &F) : f_(F) {
    // Compute W_i(\beta_j) for all i, j.

    // We store the unnormalized W_[i][j] = W_i(\beta_j)
    // in the same memory as the normalized \hat{W}_i(\beta_j), since
    // the unnormalized values are not needed after normalization.

    // In an attempt to improve clarity, we syntactically distinguish
    // the unnormalized array W from the normalized array w_hat_,
    // but one must be mindful that the two names alias to the
    // same memory locations.
    auto W = w_hat_;

    // Base case: W_0(X) = X
    for (size_t j = 0; j < kSubFieldBits; ++j) {
      W[0][j] = f_.beta(j);
    }

    // Inductive case: W_{i+1}(X) = W_i(X)(W_i(X)+W_i(\beta_i))
    for (size_t i = 0; i + 1 < kSubFieldBits; ++i) {
      for (size_t j = 0; j < kSubFieldBits; ++j) {
        W[i + 1][j] = f_.mulf(W[i][j], f_.addf(W[i][j], W[i][i]));
      }
    }

    // normalized \hat{W}_i(\beta j)
    for (size_t i = 0; i < kSubFieldBits; ++i) {
      Elt scale = f_.invertf(W[i][i]);
      for (size_t j = 0; j < kSubFieldBits; ++j) {
        w_hat_[i][j] = f_.mulf(scale, W[i][j]);
      }
    }
  }

  // Computation of a single twiddle factor.
  // Implicit in [LCH14, III.E], explicit in [DP24, Algorithm 2].
  Elt twiddle(size_t i, size_t u) const {
    Elt t = f_.zero();
    for (size_t k = 0; u != 0; ++k, u >>= 1) {
      if (u & 1) {
        f_.add(t, w_hat_[i][k]);
      }
    }
    return t;
  }

  // linear-time computation of all twiddles at the same time
  void twiddles(size_t i, size_t l, size_t coset, Elt tw[]) const {
    tw[0] = twiddle(i, coset);
    for (size_t k = 0; (i + 1) + k < l; ++k) {
      Elt shift = w_hat_[i][(i + 1) + k];
      for (size_t u = 0; u < (k1 << k); ++u) {
        tw[u + (k1 << k)] = f_.addf(tw[u], shift);
      }
    }
  }

  size_t ntwiddles(size_t l) const { return k1 << (l - 1); }

  // Notation from [DP24, Algorithm 2], except that we hardcode R=0
  // and add the coset parameter.
  void FFT(size_t l, size_t coset, Elt B[/* n = (1 << l) */]) const {
    check(l <= kSubFieldBits, "l <= kSubFieldBits");

    if (l > 0) {
      // space for twiddle factors
      std::vector<Elt> tw(ntwiddles(l));

      for (size_t i = l; i-- > 0;) {
        size_t s = k1 << i;
        twiddles(i, l, coset, &tw[0]);
        for (size_t u = 0; (u << (i + 1)) < (k1 << l); ++u) {
          Elt twu = tw[u];
          for (size_t v = 0; v < s; ++v) {
            butterfly_fwd(B, (u << (i + 1)) + v, s, twu);
          }
        }
      }
    }
  }

  void IFFT(size_t l, size_t coset, Elt B[/* n = (1 << l) */]) const {
    check(l <= kSubFieldBits, "l <= kSubFieldBits");

    if (l > 0) {
      // space for twiddle factors
      std::vector<Elt> tw(ntwiddles(l));

      for (size_t i = 0; i < l; ++i) {
        size_t s = k1 << i;
        twiddles(i, l, coset, &tw[0]);
        for (size_t u = 0; (u << (i + 1)) < (k1 << l); ++u) {
          Elt twu = tw[u];
          for (size_t v = 0; v < s; ++v) {
            butterfly_bwd(B, (u << (i + 1)) + v, s, twu);
          }
        }
      }
    }
  }

  void BidirectionalFFT(size_t l, size_t k, Elt B[/* n = (1 << l) */]) const {
    check(l <= kSubFieldBits, "l <= kSubFieldBits");
    bidir_recur(/*i=*/l, /*coset=*/0, k, B);
  }

  // debug access to w_hat_
  Elt WHat_DEBUG(size_t i, size_t j) const { return w_hat_[i][j]; }

 private:
  // avoid writing static_cast<size_t>(1) all the time.
  static constexpr size_t k1 = 1;

  const Field &f_;

  // precomputed [i][j] -> \hat{W}(\beta_j)
  Elt w_hat_[kSubFieldBits][kSubFieldBits];

  // The algorithm described in Joris van der Hoeven, "The Truncated
  // Fourier Transform and Applications".  This implementation is
  // based on the pseudo-code from the followup paper "Notes on the
  // Truncated Fourier Transform", also by Joris van der Hoeven.
  //
  // Van der Hoeven considers the classic multiplicative FFT;
  // here we port the algorithm to the [LCH14] adaptive FFT.

  // Here we call the algorithm the "Bidirectional FFT", because
  // the algorithm takes a set of points in the "time" domain
  // and the complementary set of points in the "frequency" domain,
  // and it flips time and frequency, so the algorithm can be
  // used to compute the forward and backward transforms, as well
  // as combinations of the two.
  //
  // The literature on the truncated Fourier transforms assumes that
  // the complementary set of points are implicitly set to zero, and
  // the main problem is how to avoid storing the zeroes.  Our main
  // problem is not time or space efficiency, but polynomial
  // interpolation.  Given k evaluations of a polynomial of degree <k,
  // compute the other evaluations up to n=2^l.  So we care about both
  // the unknown nonzero coefficients and the unknown n-k evaluations.
  void bidir_recur(size_t i, size_t coset, size_t k,
                   Elt B[/* n = (1 << i) */]) const {
    if (i-- > 0) {
      size_t s = k1 << i;
      Elt twu = twiddle(i, coset);

      if (k < s) {
        for (size_t uv = k; uv < s; ++uv) {
          butterfly_fwd(B, uv, s, twu);
        }

        bidir_recur(i, coset, k, B);

        for (size_t uv = 0; uv < k; ++uv) {
          butterfly_diag(B, uv, s, twu);
        }

        FFT(i, coset + s, B + s);
      } else /* k >= s */ {
        IFFT(i, coset, B);

        for (size_t uv = k - s; uv < s; ++uv) {
          butterfly_diag(B, uv, s, twu);
        }

        bidir_recur(i, coset + s, k - s, B + s);

        for (size_t uv = 0; uv < k - s; ++uv) {
          butterfly_bwd(B, uv, s, twu);
        }
      }
    }
  }

  inline void butterfly_fwd(Elt B[], size_t uv, size_t s,
                            const Elt &twu) const {
    f_.add(B[uv], f_.mulf(twu, B[uv + s]));
    f_.add(B[uv + s], B[uv]);
  }

  inline void butterfly_bwd(Elt B[], size_t uv, size_t s,
                            const Elt &twu) const {
    f_.sub(B[uv + s], B[uv]);
    f_.sub(B[uv], f_.mulf(twu, B[uv + s]));
  }

  // forward at [uv + s], backward at [uv]
  inline void butterfly_diag(Elt B[], size_t uv, size_t s,
                             const Elt &twu) const {
    Elt b1 = B[uv + s];
    f_.add(B[uv + s], B[uv]);
    f_.sub(B[uv], f_.mulf(twu, b1));
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_GF2K_LCH14_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ARRAYS_DENSE_H_
#define PRIVACY_PROOFS_ZK_LIB_ARRAYS_DENSE_H_

#include <stddef.h>
#include <string.h>

#include <array>
#include <cstdint>
#include <memory>
#include <vector>

#include "algebra/blas.h"
#include "algebra/poly.h"
#include "arrays/affine.h"
#include "util/panic.h"

namespace proofs {
// ------------------------------------------------------------
// Dense representation of multi-affine function, heap-allocated.
// The caller is responsible for instantiating const Field throughout call
// duration.
template <class Field>
class Dense {
  using T2 = Poly<2, Field>;
  using Elt = typename Field::Elt;

 public:
  corner_t n0_, n1_;

  // Row-major indexing: v_[i1*n0+i0] stores the value at (i0, i1)
  std::vector<Elt> v_;

  explicit Dense(corner_t n0, corner_t n1) : n0_(n0), n1_(n1), v_(n0 * n1) {}

  // make0 replacement
  explicit Dense(const Field& F) : n0_(1), n1_(1), v_(1) { v_[0] = F.zero(); }

  // initialize dense array from P[i1*ldp+i0]
  explicit Dense(corner_t n0, corner_t n1, const Elt p[], size_t ldp)
      : n0_(n0), n1_(n1), v_(n0 * n1) {
    for (corner_t i1 = 0; i1 < n1; ++i1) {
      Blas<Field>::copy(n0, v_[i1 * n0], 1, &p[i1 * ldp], 1);
    }
  }

  Dense(const Dense& y) = delete;
  Dense(const Dense&& y) = delete;
  Dense operator=(const Dense& y) = delete;

  std::unique_ptr<Dense> clone() const {
    auto d = std::make_unique<Dense>(n0_, n1_);
    for (corner_t i = 0; i < n0_ * n1_; ++i) {
      d->v_[i] = v_[i];
    }
    return d;
  }

  void clear(const Field& F) { Blas<Field>::clear(n0_ * n1_, &v_[0], 1, F); }

  // For a given random number r, the binding operation computes
  //   v[i] = (1 - r) * v[2 * i] + r * v[2 * i + 1]
  //        = v[2 * i] + r * (v[2 * i + 1] - v[2 * i])
  // and shrinks the array v by half.
  void bind(const Elt& r, const Field& F) {
    corner_t rd = 0, wr = 0;
    for (corner_t i1 = 0; i1 < n1_; ++i1) {
      corner_t i0 = 0;
      while (2 * i0 + 1 < n0_) {
        v_[wr] = affine_interpolation(r, v_[rd], v_[rd + 1], F);
        i0++, rd += 2, wr += 1;
      }
      if (2 * i0 < n0_) {
        v_[wr] = affine_interpolation(r, v_[rd], F.zero(), F);
        i0++, rd++, wr++;
      }
    }
    n0_ = (n0_ + 1u) / 2u;
  }

  void bind_all(size_t logv, const Elt r[/*logv*/], const Field& F) {
    for (size_t v = 0; v < logv; ++v) {
      bind(r[v], F);
    }
  }

  Elt at(corner_t j) const { return v_[j]; }

  // Scale all elements by x, except for the last element in
  // the n0_ dimension, which is scaled by x_last.  This "last" quirk
  // is used by EQ.
  void scale(const Elt& x, const Elt& x_last, const Field& F) {
    corner_t ndx = 0;
    for (corner_t i1 = 0; i1 < n1_; ++i1) {
      corner_t i0 = 0;
      for (; i0 + 1 < n0_; ++i0) {
        F.mul(v_[ndx++], x);
      }
      if (i0 < n0_) {
        F.mul(v_[ndx++], x_last);
      }
    }
  }

  Elt at_corners(corner_t p0, corner_t p1, const Field& F) const {
    if (p0 < n0_) {
      return v_[p1 * n0_ + p0];
    } else {
      return F.zero();
    }
  }

  T2 t2_at_corners(corner_t p0, corner_t p1, const Field& F) const {
    return T2{at_corners(p0, p1, F), at_corners(p0 + 1, p1, F)};
  }

  // The precondition for reshaping is that the first dimension must be
  // fully bound.
  void reshape(corner_t n0) {
    check(n0_ == 1, "n0_ == 1");
    check(n0 > 0, "n0 > 0");
    corner_t wasn1 = n1_;
    n0_ = n0;
    n1_ = n1_ / n0;
    check(n1_ * n0 == wasn1, "n1_*n0 == wasn1");
  }

  // This method can only be called after full binding; the caller
  // is responsible for ensuring that pre-condition.
  Elt scalar() {
    check(n0_ == 1, "n0_ == 1");
    check(n1_ == 1, "n1_ == 1");
    return v_[0];
  }
};

// Helper class to fill a dense array a la std::vector<>
//
template <class Field>
class DenseFiller {
  using Elt = typename Field::Elt;

 public:
  // Caller must ensure that W remains valid.
  explicit DenseFiller(Dense<Field>& W) : pos_(0), w_(W) {
    // only works in this special case
    check(w_.n0_ == 1, "W_.n0_ == 1");
  }

  DenseFiller& push_back(const Elt& x) {
    check(pos_ < w_.n1_, "pos_ < w_.n1_");
    w_.v_[pos_++] = x;
    return *this;
  }

  template <size_t N>
  DenseFiller& push_back(const std::array<Elt, N>& a) {
    for (size_t i = 0; i < N; ++i) {
      push_back(a[i]);
    }
    return *this;
  }

  DenseFiller& push_back(const std::vector<Elt>& a) {
    for (size_t i = 0; i < a.size(); ++i) {
      push_back(a[i]);
    }
    return *this;
  }

  // Push back a bit string derived from a number.  The parameter "bits" is the
  // number of bits in the string, and "x" is the number to be converted. This
  // works for pushing v8, v32, etc.
  DenseFiller& push_back(uint64_t x, size_t bits, const Field& F) {
    for (size_t i = 0; i < bits; ++i) {
      push_back(F.of_scalar((x >> i) & 1));
    }
    return *this;
  }

  size_t size() const { return pos_; }

 private:
  size_t pos_;
  Dense<Field>& w_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ARRAYS_DENSE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_PDQHASH_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_PDQHASH_H_

#include <stddef.h>
#include <stdint.h>

#include <functional>
#include <vector>

#define DEFINE_STRONG_INT_TYPE(a, b) using a = b

namespace proofs {

// Old-school, quick and dirty hash table specialized for
// uint64_t key, value_t value.  Support multiple keys
// ("multimap").
//
// This is supposed to solve the same problem as the C++
// unordered_multimap, except that the unordered_multimap
// stores key/value as a linked list and leaves the malloc()
// arena so fragmented that malloc_coalesce() takes several
// hundred ms to reconstruct the heap.
class PdqHash {
 public:
  // value of NIL denotes empty slot
  using value_t = uint32_t;
  static const constexpr value_t kNil = ~static_cast<value_t>(0);

  // Store the key as uint32_t to save space.  This is ok because
  // "key" is not really a key.  Instead, find() invokes pred() which
  // compares against the full key (stored outside this class).
  DEFINE_STRONG_INT_TYPE(stored_key_t, uint32_t);

  static stored_key_t narrow(uint64_t k) { return stored_key_t(k + (k >> 32)); }

  struct kv {
    stored_key_t k;
    value_t v;

    kv() : k(stored_key_t(0)), v(kNil) {}
  };

  PdqHash() : bits_(10), sz_(0), table_(capacity()) {}

  void insert(uint64_t k64, value_t v) {
    if (2 * sz_ > capacity()) {
      rehash();
    }
    insert0(narrow(k64), v);
  }

  size_t find(uint64_t k64, const std::function<bool(value_t)> &pred) {
    stored_key_t k = narrow(k64);
    size_t mask = (size_t(1) << bits_) - 1;
    size_t dh = dhash(k);
    for (size_t h = hash(k);; h += dh) {
      const kv *p = &table_[h & mask];
      if (p->v == kNil) {
        // not found
        return kNil;
      }
      if (p->k == k && pred(p->v)) {
        // found
        return p->v;
      }
    }
  }

 private:
  void insert0(stored_key_t k, value_t v) {
    size_t mask = (size_t(1) << bits_) - 1;
    size_t dh = dhash(k);
    for (size_t h = hash(k);; h += dh) {
      kv *p = &table_[h & mask];
      if (p->v == kNil) {
        p->k = k;
        p->v = v;
        ++sz_;
        return;
      }
    }
  }

  // Adhoc hash function suffices for this application.
  uint64_t hash(uint64_t k) {
    return k + 3 * (k >> bits_) + 7 * (k >> (2 * bits_));
  }
  uint64_t hash(stored_key_t nk) { return hash(static_cast<uint64_t>(nk)); }
  uint64_t dhash(stored_key_t nk) {
    // If gcd(dhash, capacity()) == 1, the insert loop does not have a short
    // cycle.
    return 2 * hash(hash(nk)) + 1;
  }
  void rehash() {
    ++bits_;
    std::vector<kv> table1(capacity());
    table_.swap(table1);
    for (const auto &p : table1) {
      if (p.v != kNil) {
        insert0(p.k, p.v);
      }
    }
  }

  size_t capacity() { return size_t(1) << bits_; }

  size_t bits_;
  size_t sz_;
  std::vector<kv> table_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_PDQHASH_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_PROVER_H_
#define PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_PROVER_H_

#include <stddef.h>

#include <algorithm>
#include <array>
#include <vector>

#include "algebra/blas.h"
#include "ligero/ligero_param.h"
#include "ligero/ligero_transcript.h"
#include "merkle/merkle_commitment.h"
#include "random/random.h"
#include "random/transcript.h"
#include "util/crypto.h"
#include "util/panic.h"

namespace proofs {
template <class Field, class InterpolatorFactory>
class LigeroProver {
  using Elt = typename Field::Elt;

 public:
  explicit LigeroProver(const LigeroParam<Field> &p)
      : p_(p), mc_(p.block_enc - p.dblock), tableau_(p.nrow * p.block_enc) {}

  // The SUBFIELD_BOUNDARY parameter is kind of a hack.
  //
  // Most, but not all, witnesses in W[] are known statically to be in
  // the subfield of Field, for example because they are bits or
  // bit-plucked values in the subfield.  For zero-knowledge, for
  // these witnesses, it suffices to choose blinding randomness in the
  // subfield, which yields a shorter proof since most column openings
  // are fully in the subfield.  The problem is now to distinguish
  // subfield witnesses from field witnesses.
  //
  // In the fullness of time we should have a compiler with typing
  // information (field vs subfield) of all input wires.  For now
  // we implement the following hack: W[i] is in the subfield for
  // i < SUBFIELD_BOUNDARY, and in the full field otherwise.
  // If you don't know better, set SUBFIELD_BOUNDARY = 0 which
  // trivially works for any input.
  void commit(LigeroCommitment<Field> &commitment, Transcript &ts,
              const Elt W[/*p_.nw*/], const size_t subfield_boundary,
              const LigeroQuadraticConstraint lqc[/*nq*/],
              const InterpolatorFactory &interpolator, RandomEngine &rng,
              const Field &F) {
    // Paranoid check on the SUBFIELD_BOUNDARY correctness condition
    for (size_t i = 0; i < subfield_boundary; ++i) {
      check(F.in_subfield(W[i]), "element not in subfield");
    }

    layout(W, subfield_boundary, lqc, interpolator, rng, F);

    // Merkle commitment
    auto updhash = [&](size_t j, SHA256 &sha) {
      LigeroCommon<Field>::column_hash(p_.nrow, &tableau_at(0, j + p_.dblock),
                                       p_.block_enc, sha, F);
    };
    commitment.root = mc_.commit(updhash, rng);

    // P -> V
    LigeroTranscript<Field>::write_commitment(commitment, ts);
  }

  // HASH_OF_LLTERM is a hash of LLTERM provided by the caller.  We
  // could compute the hash locally, but usually LLTERM has a special
  // structure that makes the computation faster on the caller's side.
  void prove(LigeroProof<Field> &proof, Transcript &ts, size_t nl,
             size_t nllterm,
             const LigeroLinearConstraint<Field> llterm[/*nllterm*/],
             const LigeroHash &hash_of_llterm,
             const LigeroQuadraticConstraint lqc[/*nq*/],
             const InterpolatorFactory &interpolator, const Field &F) {
    {
      // P -> V
      // theorem statement
      ts.write(hash_of_llterm.bytes, hash_of_llterm.kLength);
    }

    {
      std::vector<Elt> u_ldt(p_.nwqrow);

      // V -> P
      LigeroTranscript<Field>::gen_uldt(&u_ldt[0], p_, ts, F);
      low_degree_proof(&proof.y_ldt[0], &u_ldt[0], F);
    }

    {
      std::vector<Elt> alphal(nl);
      std::vector<std::array<Elt, 3>> alphaq(p_.nq);
      std::vector<Elt> A(p_.nwqrow * p_.w);

      // V -> P
      LigeroTranscript<Field>::gen_alphal(nl, &alphal[0], ts, F);
      LigeroTranscript<Field>::gen_alphaq(&alphaq[0], p_, ts, F);

      LigeroCommon<Field>::inner_product_vector(&A[0], p_, nl, nllterm, llterm,
                                                &alphal[0], lqc, &alphaq[0], F);

      dot_proof(&proof.y_dot[0], &A[0], interpolator, F);
    }

    {
      std::vector<Elt> u_quad(p_.nqtriples);

      // V -> P
      LigeroTranscript<Field>::gen_uquad(&u_quad[0], p_, ts, F);
      quadratic_proof(&proof.y_quad_0[0], &proof.y_quad_2[0], &u_quad[0], F);
    }

    {
      // P -> V
      ts.write(&proof.y_ldt[0], 1, p_.block, F);
      ts.write(&proof.y_dot[0], 1, p_.dblock, F);
      ts.write(&proof.y_quad_0[0], 1, p_.r, F);
      ts.write(&proof.y_quad_2[0], 1, p_.dblock - p_.block, F);
    }

    {
      std::vector<size_t> idx(p_.nreq);
      // V -> P
      LigeroTranscript<Field>::gen_idx(&idx[0], p_, ts, F);

      compute_req(proof, &idx[0]);

      mc_.open(proof.merkle, &idx[0], p_.nreq);
    }
  }

 private:
  Elt &tableau_at(size_t i, size_t j) {
    size_t ld = p_.block_enc;
    return tableau_[i * ld + j];
  }

  // fill t_[i, [0,n)] with random elements
  // If the base_only flag is true, then the random element is chosen from
  // the base field if F is a field extension.
  void random_row(size_t i, size_t n, RandomEngine &rng, const Field &F) {
    for (size_t j = 0; j < n; ++j) {
      tableau_at(i, j) = rng.elt(F);
    }
  }

  void random_subfield_row(size_t i, size_t n, RandomEngine &rng,
                           const Field &F) {
    for (size_t j = 0; j < n; ++j) {
      tableau_at(i, j) = rng.subfield_elt(F);
    }
  }

  // generate the ILDT and IDOT blinding rows
  void layout_blinding_rows(const InterpolatorFactory &interpolator,
                            RandomEngine &rng, const Field &F) {
    {
      // blinds of size [BLOCK]
      const auto interp = interpolator.make(p_.block, p_.block_enc);

      // low-degree blinding row
      random_row(p_.ildt, p_.block, rng, F);
      interp->interpolate(&tableau_at(p_.ildt, 0));
    }

    {
      // blinds of size [DBLOCK]
      const auto interp = interpolator.make(p_.dblock, p_.block_enc);

      // dot-product blinding row constrained to SUM(W) = 0. First
      // randomize the dblock:
      random_row(p_.idot, p_.dblock, rng, F);

      // Then constrain to sum(W) = 0
      Elt sum = Blas<Field>::dot1(p_.w, &tableau_at(p_.idot, p_.r), 1, F);
      F.sub(tableau_at(p_.idot, p_.r), sum);

      interp->interpolate(&tableau_at(p_.idot, 0));

      // quadratic-test blinding row constrained to W = 0.  First
      // randomize the entire dblock:
      random_row(p_.iquad, p_.dblock, rng, F);

      // Then constrain to W = 0
      Blas<Field>::clear(p_.w, &tableau_at(p_.iquad, p_.r), 1, F);

      interp->interpolate(&tableau_at(p_.iquad, 0));
    }
  }

  void layout_witness_rows(const Elt W[/*nw*/], size_t subfield_boundary,
                           const InterpolatorFactory &interpolator,
                           RandomEngine &rng, const Field &F) {
    const auto interp = interpolator.make(p_.block, p_.block_enc);

    // witness row EXTEND([RANDOM[R], WITNESS[W]], BLOCK)
    for (size_t i = 0; i < p_.nwrow; ++i) {
      // TRUE if the entire row is in the subfield
      bool subfield_only = ((i + 1) * p_.w <= subfield_boundary);

      if (subfield_only) {
        random_subfield_row(i + p_.iw, p_.r, rng, F);
      } else {
        random_row(i + p_.iw, p_.r, rng, F);
      }

      // Set the WITNESS columns to zero first, and then
      // overwrite with the witnesses that actually exist
      Blas<Field>::clear(p_.w, &tableau_at(i + p_.iw, p_.r), 1, F);
      size_t max_col = std::min(p_.w, p_.nw - i * p_.w);
      Blas<Field>::copy(max_col, &tableau_at(i + p_.iw, p_.r), 1, &W[i * p_.w],
                        1);
      interp->interpolate(&tableau_at(i + p_.iw, 0));
    }
  }

  void layout_quadratic_rows(const Elt W[/*nw*/],
                             const LigeroQuadraticConstraint lqc[/*nq*/],
                             const InterpolatorFactory &interpolator,
                             RandomEngine &rng, const Field &F) {
    const auto interp = interpolator.make(p_.block, p_.block_enc);

    // copy the multiplicand witnesses into the quadratic rows
    size_t iqx = p_.iq;
    size_t iqy = iqx + p_.nqtriples;
    size_t iqz = iqy + p_.nqtriples;

    for (size_t i = 0; i < p_.nqtriples; ++i) {
      random_row(iqx + i, p_.r, rng, F);
      random_row(iqy + i, p_.r, rng, F);
      random_row(iqz + i, p_.r, rng, F);

      // clear everything first, then overwrite the witnesses that
      // actually exist
      Blas<Field>::clear(p_.w, &tableau_at(iqx + i, p_.r), 1, F);
      Blas<Field>::clear(p_.w, &tableau_at(iqy + i, p_.r), 1, F);
      Blas<Field>::clear(p_.w, &tableau_at(iqz + i, p_.r), 1, F);

      for (size_t j = 0; j < p_.w && j + i * p_.w < p_.nq; ++j) {
        const auto *l = &lqc[j + i * p_.w];
        check(W[l->z] == F.mulf(W[l->x], W[l->y]),
              "invalid quadratic constraints");
        tableau_at(iqx + i, j + p_.r) = W[l->x];
        tableau_at(iqy + i, j + p_.r) = W[l->y];
        tableau_at(iqz + i, j + p_.r) = W[l->z];
      }
      interp->interpolate(&tableau_at(iqx + i, 0));
      interp->interpolate(&tableau_at(iqy + i, 0));
      interp->interpolate(&tableau_at(iqz + i, 0));
    }
  }

  void layout(const Elt W[/*nw*/], size_t subfield_boundary,
              const LigeroQuadraticConstraint lqc[/*nq*/],
              const InterpolatorFactory &interpolator, RandomEngine &rng,
              const Field &F) {
    layout_blinding_rows(interpolator, rng, F);
    layout_witness_rows(W, subfield_boundary, interpolator, rng, F);
    layout_quadratic_rows(W, lqc, interpolator, rng, F);
  }

  void low_degree_proof(Elt y[/*block*/], const Elt u_ldt[/*nwqrow*/],
                        const Field &F) {
    // ILDT blinding row with coefficient 1
    Blas<Field>::copy(p_.block, y, 1, &tableau_at(p_.ildt, 0), 1);

    // all witness and quadratic rows with coefficient u_ldt[]
    for (size_t i = 0; i < p_.nwqrow; ++i) {
      Blas<Field>::axpy(p_.block, y, 1, u_ldt[i], &tableau_at(i + p_.iw, 0), 1,
                        F);
    }
  }

  void dot_proof(Elt y[/*dblock*/], const Elt A[/*nwqrow, w*/],
                 const InterpolatorFactory &interpolator, const Field &F) {
    const auto interpA = interpolator.make(p_.block, p_.dblock);

    // IDOT blinding row with coefficient 1
    Blas<Field>::copy(p_.dblock, y, 1, &tableau_at(p_.idot, 0), 1);

    std::vector<Elt> Aext(p_.dblock);
    for (size_t i = 0; i < p_.nwqrow; ++i) {
      LigeroCommon<Field>::layout_Aext(&Aext[0], p_, i, &A[0], F);
      interpA->interpolate(&Aext[0]);

      // Accumulate y += A \otimes W.
      Blas<Field>::vaxpy(p_.dblock, &y[0], 1, &Aext[0], 1,
                         &tableau_at(i + p_.iw, 0), 1, F);
    }
  }

  void quadratic_proof(Elt y0[/*r*/], Elt y2[/*dblock - block*/],
                       const Elt u_quad[/*nqtriples*/], const Field &F) {
    std::vector<Elt> y(p_.dblock);
    std::vector<Elt> tmp(p_.dblock);

    // IQUAD blinding row with coefficient 1
    Blas<Field>::copy(p_.dblock, &y[0], 1, &tableau_at(p_.iquad, 0), 1);

    size_t iqx = p_.iq;
    size_t iqy = iqx + p_.nqtriples;
    size_t iqz = iqy + p_.nqtriples;

    for (size_t i = 0; i < p_.nqtriples; ++i) {
      // y += u_quad[i] * (z[i] - x[i] * y[i])

      // tmp = z[i]
      Blas<Field>::copy(p_.dblock, &tmp[0], 1, &tableau_at(iqz + i, 0), 1);

      // tmp -= x[i] \otimes y[i]
      Blas<Field>::vymax(p_.dblock, &tmp[0], 1, &tableau_at(iqx + i, 0), 1,
                         &tableau_at(iqy + i, 0), 1, F);

      // y += u_quad[i] * tmp
      Blas<Field>::axpy(p_.dblock, &y[0], 1, u_quad[i], &tmp[0], 1, F);
    }

    // sanity check: the W part of Y is zero
    bool ok = Blas<Field>::equal0(p_.w, &y[p_.r], 1, F);
    check(ok, "W part is nonzero");

    // extract the first and last parts
    Blas<Field>::copy(p_.r, y0, 1, &y[0], 1);
    Blas<Field>::copy(p_.dblock - p_.block, y2, 1, &y[p_.block], 1);
  }

  void compute_req(LigeroProof<Field> &proof, const size_t idx[/*nreq*/]) {
    for (size_t i = 0; i < p_.nrow; ++i) {
      Blas<Field>::gather(p_.nreq, &proof.req_at(i, 0),
                          &tableau_at(i, p_.dblock), idx);
    }
  }

  const LigeroParam<Field> p_; /* safer to make copy */
  MerkleCommitment mc_;
  std::vector<Elt> tableau_ /*[nrow, block_enc]*/;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_PROVER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ARRAYS_SPARSE_H_
#define PRIVACY_PROOFS_ZK_LIB_ARRAYS_SPARSE_H_

#include <stddef.h>

#include <algorithm>
#include <memory>
#include <vector>

#include "algebra/compare.h"
#include "algebra/poly.h"
#include "arrays/affine.h"
#include "util/panic.h"

namespace proofs {
// ------------------------------------------------------------
// Sparse representation of multi-affine functions.
//
// This class is mainly used as a reference implementation
// for testing, and it exposes a similar interface as dense<Field>.
// Sumcheck has its own specialized "quad" implementation.
//
template <class Field>
class Sparse {
  using Elt = typename Field::Elt;
  using T2 = Poly<2, Field>;

 public:
  // A corner on the sparse hypercube, represented as triple of size_t
  // and a value.  The 3D representation is kind of a guess of how
  // many bits we'll ever need.  Under the theory that "size_t" has
  // enough bits to index a dense array that fills the address space,
  // and that the program should support |points| gates, and each gate
  // has three terminals, then a triple ought to be both necessary and
  // sufficient.
  struct corner {
    size_t p0, p1, p2;
    Elt v;

    bool eqndx(const corner& y) const {
      return (p2 == y.p2 && p1 == y.p1 && p0 == y.p0);
    }
    bool operator==(const corner& y) const { return eqndx(y) && v == y.v; }
    bool operator!=(const corner& y) const { return !operator==(y); }

    static bool compare(const corner& x, const corner& y, const Field& F) {
      if (x.p2 < y.p2) return true;
      if (x.p2 > y.p2) return false;
      if (x.p1 < y.p1) return true;
      if (x.p1 > y.p1) return false;
      if (x.p0 < y.p0) return true;
      if (x.p0 > y.p0) return false;
      return elt_less_than(x.v, y.v, F);
    }
  };

  // the index of a point in a sparse array
  using index_t = size_t;

  index_t n_;
  std::vector<corner> c_;

  explicit Sparse(index_t n) : n_(n), c_(n) {}

  // no copies, but see clone() below
  Sparse(const Sparse& y) = delete;
  Sparse(const Sparse&& y) = delete;
  Sparse operator=(const Sparse& y) = delete;

  // Nobody should need to clone a sparse array except tests.
  // Reflect this fact in the name.
  std::unique_ptr<Sparse> clone_testing_only() const {
    auto s = std::make_unique<Sparse>(n_);
    for (index_t i = 0; i < n_; ++i) {
      s->c_[i] = c_[i];
    }
    return s;
  }

  T2 t2_at_corners(index_t* newi, index_t i, const Field& F) const {
    // If c_[i] and c_[i+1] have the same (P2, P1), and they differ
    // by the least-significant bit in P0:
    if (i + 1 < n_ &&                              //
        c_[i].p2 == c_[i + 1].p2 &&                //
        c_[i].p1 == c_[i + 1].p1 &&                //
        (c_[i].p0 >> 1) == (c_[i + 1].p0 >> 1) &&  //
        c_[i + 1].p0 == c_[i].p0 + 1) {
      // we have two corners.
      *newi = i + 2;
      return T2{c_[i].v, c_[i + 1].v};
    } else {
      // we have one corner and the other one is zero.
      *newi = i + 1;
      if ((c_[i].p0 & 1) == 0) {
        return T2{c_[i].v, F.zero()};
      } else {
        return T2{F.zero(), c_[i].v};
      }
    }
  }

  // For a given random number r, the binding operation computes
  //   v[p2, p1, p0] = (1 - r) * v[p2, p1, 2 * p0] + r * v[p2, p1, 2 * p0 + 1]
  // Note that either the odd or the even element or both may not be actually
  // present in the sparse array.
  void bind(const Elt& r, const Field& F) {
    index_t rd = 0, wr = 0;
    while (rd < n_) {
      index_t newrd;
      T2 f = t2_at_corners(&newrd, rd, F);
      c_[wr] = corner{.p0 = c_[rd].p0 >> 1,
                      .p1 = c_[rd].p1,
                      .p2 = c_[rd].p2,
                      .v = affine_interpolation(r, f.t_[0], f.t_[1], F)};
      wr++;
      rd = newrd;
    }

    // shrink the array
    n_ = wr;
  }

  void bind_all(size_t logv, const Elt r[/*logv*/], const Field& F) {
    for (size_t v = 0; v < logv; ++v) {
      bind(r[v], F);
    }
  }

  void reshape() {
    // this function works only if c_[i].p0 == 0 for all i, but
    // rather than checking them one at the time, keep a giant
    // bitwise OR and check at the end
    size_t lost_bits = 0;
    for (index_t i = 0; i < n_; ++i) {
      lost_bits |= c_[i].p0;
      c_[i] = corner{.p0 = c_[i].p1, .p1 = c_[i].p2, .p2 = 0, .v = c_[i].v};
    }
    check(lost_bits == 0, "lost_bits == 0");
  }

  // This method can only be called after full binding; the caller
  // is responsible for ensuring that pre-condition.
  Elt scalar() {
    check(n_ == 1, "n_ == 1");
    check(c_[0].p0 == 0, "c_[0].p0_ == 0");
    check(c_[0].p1 == 0, "c_[0].p1_ == 0");
    check(c_[0].p2 == 0, "c_[0].p2_ == 0");
    return c_[0].v;
  }

  void canonicalize(const Field& F) {
    std::sort(c_.begin(), c_.end(), [&F](const corner& x, const corner& y) {
      return corner::compare(x, y, F);
    });
    return coalesce(F);
  }

 private:
  void coalesce(const Field& F) {
    // Coalesce duplicates.
    // The (rd,wr)=(0,0) iteration executes the else{} branch and
    // continues with (1,1), so we start at (1,1) and avoid the
    // special case for wr-1 at wr=0.
    index_t wr = 1;
    for (index_t rd = 1; rd < n_; ++rd) {
      if (c_[rd].eqndx(c_[wr - 1])) {
        F.add(c_[wr - 1].v, c_[rd].v);
      } else {
        c_[wr] = c_[rd];
        wr++;
      }
    }
    n_ = wr;
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ARRAYS_SPARSE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP2_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP2_H_

#include <stddef.h>

#include <cstdint>
#include <optional>

#include "util/panic.h"

namespace proofs {
// Fields of the form a+sqrt(r)*b where a, b \in Fp and
// r is a quadratic nonresidue in Fp.  The special "complex"
// case r = -1 allows for a faster implementation of multiplication.
//
// With slight abuse of terminology, we call "a" the "real" part and
// "b" the "imaginary" part, and we call the sqrt(r) "i" even when
// r != -1.
template <class Field, bool nonresidue_is_mone = true>
class Fp2 {
 public:
  using Scalar = typename Field::Elt;
  using BaseField = Field;
  using TypeTag = typename Field::TypeTag;

  // size of the serialization into bytes
  static constexpr size_t kBytes = 2 * Field::kBytes;
  static constexpr size_t kBits = 2 * Field::kBits;
  static constexpr size_t kSubFieldBytes = Field::kBytes;
  static constexpr bool kCharacteristicTwo = false;
  const Field& f_;

  struct Elt {
    Scalar re, im;
    bool operator==(const Elt& y) const { return re == y.re && im == y.im; }
    bool operator!=(const Elt& y) const { return !operator==(y); }
  };

  explicit Fp2(const Field& F, const Scalar& nonresidue)
      : f_(F), nonresidue_(nonresidue) {
    if (nonresidue_is_mone) {
      check(nonresidue == F.mone(), "nonresidue == F.mone()");
    } else {
      check(nonresidue != F.mone(), "nonresidue != F.mone()");
    }

    i_ = Elt{f_.zero(), f_.one()};
    for (uint64_t i = 0; i < sizeof(k_) / sizeof(k_[0]); ++i) {
      k_[i] = of_scalar(i);
    }
    khalf_ = Elt{f_.half(), f_.zero()};
    kmone_ = Elt{f_.mone(), f_.zero()};
  }
  explicit Fp2(const Field& F) : Fp2(F, F.mone()) {}

  Fp2(const Fp2&) = delete;
  Fp2& operator=(const Fp2&) = delete;

  const Field& base_field() const { return f_; }

  Scalar real(const Elt& e) const { return e.re; }
  bool is_real(const Elt& e) const { return e.im == f_.zero(); }

  void add(Elt& a, const Elt& y) const {
    f_.add(a.re, y.re);
    f_.add(a.im, y.im);
  }
  void sub(Elt& a, const Elt& y) const {
    f_.sub(a.re, y.re);
    f_.sub(a.im, y.im);
  }
  void mul(Elt& a, const Elt& y) const {
    auto p0 = f_.mulf(a.re, y.re);
    auto p1 = f_.mulf(a.im, y.im);
    auto a01 = f_.addf(a.re, a.im);
    auto y01 = f_.addf(y.re, y.im);
    if (nonresidue_is_mone) {
      a.re = f_.subf(p0, p1);
    } else {
      a.re = f_.addf(p0, f_.mulf(p1, nonresidue_));
    }
    f_.mul(a01, y01);
    f_.sub(a01, p0);
    f_.sub(a01, p1);
    a.im = a01;
  }
  void mul(Elt& a, const Scalar& y) const {
    f_.mul(a.re, y);
    f_.mul(a.im, y);
  }
  void neg(Elt& x) const {
    Elt y(k_[0]);
    sub(y, x);
    x = y;
  }
  void conj(Elt& x) const { f_.neg(x.im); }
  void invert(Elt& x) const {
    Scalar denom;
    if (nonresidue_is_mone) {
      denom = f_.addf(f_.mulf(x.re, x.re), f_.mulf(x.im, x.im));
    } else {
      denom = f_.subf(f_.mulf(x.re, x.re),
                      f_.mulf(nonresidue_, f_.mulf(x.im, x.im)));
    }
    f_.invert(denom);
    conj(x);
    mul(x, denom);
  }

  // functional interface
  Elt addf(Elt a, const Elt& y) const {
    add(a, y);
    return a;
  }
  Elt subf(Elt a, const Elt& y) const {
    sub(a, y);
    return a;
  }
  Elt mulf(Elt a, const Elt& y) const {
    mul(a, y);
    return a;
  }
  Elt mulf(Elt a, const Scalar& y) const {
    mul(a, y);
    return a;
  }
  Elt negf(Elt a) const {
    neg(a);
    return a;
  }
  Elt invertf(Elt a) const {
    invert(a);
    return a;
  }
  Elt conjf(Elt a) const {
    conj(a);
    return a;
  }

  Elt of_scalar(uint64_t a) const { return of_scalar_field(a); }
  Elt of_scalar(const Scalar& e) const { return of_scalar_field(e); }

  Elt of_scalar_field(const Scalar& e) const { return Elt{e, f_.zero()}; }
  Elt of_scalar_field(uint64_t a) const {
    return Elt{f_.of_scalar(a), f_.zero()};
  }
  Elt of_scalar_field(uint64_t ar, uint64_t ai) const {
    return Elt{f_.of_scalar(ar), f_.of_scalar(ai)};
  }

  template <size_t N>
  Elt of_string(const char (&s)[N]) const {
    return Elt{f_.of_string(s), f_.zero()};
  }

  template <size_t NR, size_t NI>
  Elt of_string(const char (&sr)[NR], const char (&si)[NI]) const {
    return Elt{f_.of_string(sr), f_.of_string(si)};
  }

  std::optional<Elt> of_bytes_field(const uint8_t ab[/* kBytes */]) const {
    if (auto re = f_.of_bytes_field(ab)) {
      if (auto im = f_.of_bytes_field(ab + Field::kBytes)) {
        return Elt{re.value(), im.value()};
      }
    }
    return std::nullopt;
  }

  void to_bytes_field(uint8_t ab[/* kBytes */], const Elt& x) const {
    f_.to_bytes_field(ab, x.re);
    f_.to_bytes_field(ab + Field::kBytes, x.im);
  }

  bool in_subfield(const Elt& e) const { return is_real(e); }

  std::optional<Elt> of_bytes_subfield(
      const uint8_t ab[/* kSubFieldBytes */]) const {
    if (auto re = f_.of_bytes_subfield(ab)) {
      return of_scalar(re.value());
    }
    return std::nullopt;
  }

  void to_bytes_subfield(uint8_t ab[/* kSubFieldBytes */], const Elt& x) const {
    check(in_subfield(x), "x not in subfield");
    f_.to_bytes_subfield(ab, x.re);
  }

  const Elt& zero() const { return k_[0]; }
  const Elt& one() const { return k_[1]; }
  const Elt& two() const { return k_[2]; }
  const Elt& half() const { return khalf_; }
  const Elt& mone() const { return kmone_; }
  const Elt& i() const { return i_; }
  Elt poly_evaluation_point(size_t i) const {
    return of_scalar(f_.poly_evaluation_point(i));
  }
  Elt newton_denominator(size_t k, size_t i) const {
    return of_scalar(f_.newton_denominator(k, i));
  }

 private:
  Scalar nonresidue_;
  Elt k_[3];  // small constants
  Elt i_;     // i^2 = -1
  Elt khalf_;
  Elt kmone_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP2_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_ROUTING_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_ROUTING_H_

#include <stddef.h>

#include <algorithm>
#include <vector>

#include "util/ceildiv.h"
#include "util/panic.h"

namespace proofs {
/*
The Routing class implements circuits that shift an array by a variable number
of positions. The following table can help pick parameters for a shift:

shift_bit[2][2][1]    depth: 2 wires: 6 in: 4 out:2 use:6 ovh:0 t:5 cse:0 notn:7
unshift_bit[2][2][1]  depth: 2 wires: 6 in: 4 out:2 use:6 ovh:0 t:5 cse:0 notn:7
shift_bit[4][4][1]    depth: 3 wires: 17 in: 7 out:4 use:15 ovh:2 t:23 cse:0
notn:27 unshift_bit[4][4][1]  depth: 3 wires: 17 in: 7 out:4 use:15 ovh:2 t:23
cse:0 notn:27 shift_bit[4][4][2]    depth: 3 wires: 19 in: 7 out:4 use:15 ovh:4
t:23 cse:2 notn:20 unshift_bit[4][4][2]  depth: 3 wires: 19 in: 7 out:4 use:15
ovh:4 t:23 cse:2 notn:20 shift_bit[8][8][1]    depth: 4 wires: 41 in: 12 out:8
use:36 ovh:5 t:70 cse:0 notn:83 unshift_bit[8][8][1]  depth: 4 wires: 41 in: 12
out:8 use:36 ovh:5 t:70 cse:0 notn:83 shift_bit[8][8][2]    depth: 4 wires: 44
in: 12 out:8 use:32 ovh:12 t:64 cse:2 notn:62 unshift_bit[8][8][2]  depth: 4
wires: 44 in: 12 out:8 use:32 ovh:12 t:67 cse:2 notn:68 shift_bit[16][16][1]
depth: 5 wires: 94 in: 21 out:16 use:85 ovh:9 t:186 cse:0 notn:227
unshift_bit[16][16][1]  depth: 5 wires: 94 in: 21 out:16 use:85 ovh:9 t:186
cse:0 notn:227 shift_bit[16][16][2]    depth: 4 wires: 82 in: 21 out:16 use:61
ovh:21 t:137 cse:4 notn:147 unshift_bit[16][16][2]  depth: 4 wires: 82 in: 21
out:16 use:61 ovh:21 t:137 cse:4 notn:147 shift_bit[16][16][4]    depth: 4
wires: 94 in: 21 out:16 use:61 ovh:33 t:203 cse:58 notn:255
unshift_bit[16][16][4]  depth: 4 wires: 94 in: 21 out:16 use:61 ovh:33 t:203
cse:58 notn:255 shift_bit[32][32][1]    depth: 6 wires: 212 in: 38 out:32
use:198 ovh:14 t:463 cse:0 notn:579 unshift_bit[32][32][1]  depth: 6 wires: 212
in: 38 out:32 use:198 ovh:14 t:463 cse:0 notn:579 shift_bit[32][32][2]    depth:
5 wires: 184 in: 38 out:32 use:142 ovh:42 t:351 cse:4 notn:405
unshift_bit[32][32][2]  depth: 5 wires: 184 in: 38 out:32 use:142 ovh:42 t:366
cse:4 notn:435 shift_bit[32][32][4]    depth: 5 wires: 193 in: 38 out:32 use:118
ovh:75 t:371 cse:13 notn:427 unshift_bit[32][32][4]  depth: 5 wires: 193 in: 38
out:32 use:118 ovh:75 t:413 cse:13 notn:511 shift_bit[64][64][1]    depth: 7
wires: 475 in: 71 out:64 use:455 ovh:20 t:1109 cse:0 notn:1411
unshift_bit[64][64][1]  depth: 7 wires: 475 in: 71 out:64 use:455 ovh:20 t:1109
cse:0 notn:1411 shift_bit[64][64][2]    depth: 5 wires: 353 in: 71 out:64
use:275 ovh:78 t:747 cse:6 notn:922 unshift_bit[64][64][2]  depth: 5 wires: 353
in: 71 out:64 use:275 ovh:78 t:747 cse:6 notn:922 shift_bit[64][64][4]    depth:
5 wires: 363 in: 71 out:64 use:223 ovh:140 t:954 cse:22 notn:1319
unshift_bit[64][64][4]  depth: 5 wires: 363 in: 71 out:64 use:223 ovh:140 t:954
cse:22 notn:1319 shift_bit[128][128][1]  depth: 8 wires: 1059 in: 136 out:128
use:1032 ovh:27 t:2588 cse:0 notn:3331 unshift_bit[128][128][1]  depth: 8 wires:
1059 in: 136 out:128 use:1032 ovh:27 t:2588 cse:0 notn:3331
shift_bit[128][128][2]    depth: 6 wires: 808 in: 136 out:128 use:660 ovh:148
t:1842 cse:6 notn:2332 unshift_bit[128][128][2]  depth: 6 wires: 808 in: 136
out:128 use:660 ovh:148 t:1905 cse:6 notn:2458 shift_bit[128][128][4]    depth:
5 wires: 695 in: 136 out:128 use:428 ovh:267 t:2406 cse:69 notn:3686
unshift_bit[128][128][4]  depth: 5 wires: 695 in: 136 out:128 use:428 ovh:267
t:2826 cse:69 notn:4526 shift_bit[256][256][1]    depth: 9 wires: 2348 in: 265
out:256 use:2313 ovh:35 t:5924 cse:0 notn:7683 unshift_bit[256][256][1]  depth:
9 wires: 2348 in: 265 out:256 use:2313 ovh:35 t:5924 cse:0 notn:7683
shift_bit[256][256][2]    depth: 6 wires: 1588 in: 265 out:256 use:1305 ovh:283
t:3905 cse:8 notn:5153 unshift_bit[256][256][2]  depth: 6 wires: 1588 in: 265
out:256 use:1305 ovh:283 t:3905 cse:8 notn:5153 shift_bit[256][256][4]    depth:
5 wires: 1355 in: 265 out:256 use:825 ovh:530 t:6750 cse:116 notn:11309
unshift_bit[256][256][4]  depth: 5 wires: 1355 in: 265 out:256 use:825 ovh:530
t:6750 cse:116 notn:11309 shift_bit[256][256][8]    depth: 5 wires: 1595 in: 265
out:256 use:825 ovh:770 t:33990 cse:2756 notn:65309 unshift_bit[256][256][8]
depth: 5 wires: 1595 in: 265 out:256 use:825 ovh:770 t:33990 cse:2756 notn:65309
shift_bit[512][512][1]    depth: 10 wires: 5174 in: 522 out:512 use:5130 ovh:44
t:13357 cse:0 notn:17411 unshift_bit[512][512][1]  depth: 10 wires: 5174 in: 522
out:512 use:5130 ovh:44 t:13357 cse:0 notn:17411 shift_bit[512][512][2] depth: 7
wires: 3644 in: 522 out:512 use:3098 ovh:546 t:9289 cse:8 notn:12323
unshift_bit[512][512][2]  depth: 7 wires: 3644 in: 522 out:512 use:3098 ovh:546
t:9544 cse:8 notn:12833 shift_bit[512][512][4]    depth: 6 wires: 3148 in: 522
out:512 use:2094 ovh:1054 t:11361 cse:33 notn:17462 unshift_bit[512][512][4]
depth: 6 wires: 3148 in: 522 out:512 use:2094 ovh:1054 t:11361 cse:33 notn:17462
shift_bit[512][512][8]    depth: 6 wires: 3194 in: 522 out:512 use:1618 ovh:1576
t:18192 cse:224 notn:31029 unshift_bit[512][512][8]  depth: 6 wires: 3194 in:
522 out:512 use:1618 ovh:1576 t:21912 cse:224 notn:38469
shift_bit[1024][1024][1]  depth: 11 wires: 11329 in: 1035 out:1024 use:11275
ovh:54 t:29751 cse:0 notn:38915 unshift_bit[1024][1024][1]  depth: 11 wires:
11329 in: 1035 out:1024 use:11275 ovh:54 t:29751 cse:0 notn:38915
shift_bit[1024][1024][2]    depth: 7 wires: 7243 in: 1035 out:1024 use:6175
ovh:1068 t:19547 cse:10 notn:26664 unshift_bit[1024][1024][2]  depth: 7 wires:
7243 in: 1035 out:1024 use:6175 ovh:1068 t:19547 cse:10 notn:26664
shift_bit[1024][1024][4]    depth: 6 wires: 6232 in: 1035 out:1024 use:4155
ovh:2077 t:26989 cse:80 notn:43573 unshift_bit[1024][1024][4]  depth: 6 wires:
6232 in: 1035 out:1024 use:4155 ovh:2077 t:30769 cse:80 notn:51133
shift_bit[1024][1024][8]    depth: 6 wires: 6296 in: 1035 out:1024 use:3179
ovh:3117 t:52409 cse:332 notn:94285 unshift_bit[1024][1024][8]  depth: 6 wires:
6296 in: 1035 out:1024 use:3179 ovh:3117 t:52409 cse:332 notn:94285
*/
template <class Logic>
class Routing {
 public:
  typedef typename Logic::BitW bitW;
  typedef typename Logic::EltW EltW;
  const Logic& l_;

  explicit Routing(const Logic& l) : l_(l) {}

  // Set B[i] = A[i + amount], for 0 <= i < k.  Note that A and B
  // are in general of different size.
  template <class T>
  void shift(size_t logn, const bitW amount[/*logn*/], size_t k, T B[/*k*/],
             size_t n, const T A[/*n*/], const T& defaultA,
             size_t unroll) const {
    std::vector<T> tmp(n);
    for (size_t i = 0; i < n; ++i) {
      tmp[i] = A[i];
    }

    // Now shift TMP in-place.

    // Counting backwards from logn produces a smaller circuit if one
    // only cares about a contiguous subset of outputs.  E.g. if one
    // wants the first k outputs the number of wires is O(n log k).
    size_t l = logn;

    // This funny logic in terms of (target_nrounds, consumed)
    // attempts to equalize the number of bits consumed per round.
    // E.g., if logn = 11 and unroll = 7, a naive consumed = unroll
    // would yield 11 = 7 + 4.  Instead, we set target_nrounds = 2,
    // and consumed is 6 in the first round and 5 in the second round.
    size_t target_nrounds = ceildiv(logn, unroll);

    while (target_nrounds > 0) {
      size_t consumed = ceildiv(l, target_nrounds);
      --target_nrounds;

      l -= consumed;
      size_t shift = size_t(1) << l;
      shift_step(consumed, &amount[l], n, k, tmp.data(), shift, defaultA);
    }

    check(l == 0, "l==0");

    for (size_t i = 0; i < k; ++i) {
      if (i < n) {
        B[i] = tmp[i];
      } else {
        B[i] = defaultA;
      }
    }
  }

  // Set A[i + amount] = B[i], for 0 <= i < k.  Note that A and B
  // are in general of different size.
  template <class T>
  void unshift(size_t logn, const bitW amount[/*logn*/], size_t n, T A[/*n*/],
               size_t k, const T B[/*k*/], const T& defaultB,
               size_t unroll) const {
    // we don't need TMP since we can operate on A directly
    for (size_t i = 0; i < n; ++i) {
      if (i < k) {
        A[i] = B[i];
      } else {
        A[i] = defaultB;
      }
    }

    size_t l = 0;
    size_t target_nrounds = ceildiv(logn, unroll);
    while (target_nrounds > 0) {
      size_t consumed = ceildiv((logn - l), target_nrounds);
      --target_nrounds;

      size_t shift = size_t(1) << l;
      unshift_step(consumed, &amount[l], n, k, A, shift, defaultB);

      l += consumed;
    }
    proofs::check(l == logn, "l==logn");
  }

  template <class T, size_t LOGN>
  void shift(const typename Logic::template bitvec<LOGN>& amount, size_t k,
             T B[/*k*/], size_t n, const T A[/*n*/], const T& defaultA,
             size_t unroll) const {
    shift(LOGN, &amount[0], k, B, n, A, defaultA, unroll);
  }

  template <class T, size_t LOGN>
  void unshift(const typename Logic::template bitvec<LOGN>& amount, size_t n,
               T A[/*n*/], size_t k, const T B[/*k*/], const T& defaultB,
               size_t unroll) const {
    unshift(LOGN, &amount[0], n, A, k, B, defaultB, unroll);
  }

 private:
  template <class T>
  void shift_step(size_t logc, const bitW amount[/*logc*/], size_t n, size_t k,
                  T tmp[/*n*/], size_t shift, const T& defaultA) const {
    const Logic& L = l_;  // shorthand
    size_t c = size_t(1) << logc;

    // cache the common subexpression amount_is[i]
    std::vector<bitW> amount_is(c);
    std::vector<bitW> ibits(logc);
    for (size_t i = 0; i < c; ++i) {
      L.bits(logc, ibits.data(), i);
      amount_is[i] = L.eq(logc, ibits.data(), amount);
    }

    really_shift(c, amount_is.data(), n, k, tmp, shift, defaultA);
  }

  template <class T>
  void unshift_step(size_t logc, const bitW amount[/*logc*/], size_t n,
                    size_t k, T A[/*n*/], size_t shift,
                    const T& defaultB) const {
    const Logic& L = l_;  // shorthand
    size_t c = size_t(1) << logc;

    // cache the common subexpression amount_is[i]
    std::vector<bitW> amount_is(c);
    std::vector<bitW> ibits(logc);
    for (size_t i = 0; i < c; ++i) {
      L.bits(logc, ibits.data(), i);
      amount_is[i] = L.eq(logc, ibits.data(), amount);
    }

    really_unshift(c, amount_is.data(), n, k, A, shift, defaultB);
  }

  void really_shift(size_t c, const bitW amount_is[/*c*/], size_t n, size_t k,
                    EltW tmp[/*n*/], size_t shift, const EltW& defaultA) const {
    const Logic& L = l_;  // shorthand
    for (size_t i = 0; i < n && i < k + shift; ++i) {
      auto f = [&](size_t j) {
        if (i + j * shift < n) {
          return L.lmul(&amount_is[j], tmp[i + j * shift]);
        } else {
          return L.lmul(&amount_is[j], defaultA);
        }
      };

      tmp[i] = L.add(0, c, f);
    }
  }

  void really_unshift(size_t c, const bitW amount_is[/*c*/], size_t n, size_t k,
                      EltW A[/*n*/], size_t shift, const EltW& defaultB) const {
    const Logic& L = l_;  // shorthand
    for (size_t i = std::min(n, k + c * shift); i-- > 0;) {
      auto f = [&](size_t j) {
        if (i >= j * shift) {
          return L.lmul(&amount_is[j], A[i - j * shift]);
        } else {
          return L.lmul(&amount_is[j], defaultB);
        }
      };

      A[i] = L.add(0, c, f);
    }
  }

  void really_shift(size_t c, const bitW amount_is[/*c*/], size_t n, size_t k,
                    bitW tmp[/*n*/], size_t shift, const bitW& defaultA) const {
    const Logic& L = l_;  // shorthand
    for (size_t i = 0; i < n && i < k + shift; ++i) {
      bitW r = L.bit(0);
      for (size_t j = 0; j < c; ++j) {
        if (i + j * shift < n) {
          r = L.lor_exclusive(&r, L.land(&amount_is[j], tmp[i + j * shift]));
        } else {
          r = L.lor_exclusive(&r, L.land(&amount_is[j], defaultA));
        }
      }
      tmp[i] = r;
    }
  }

  void really_unshift(size_t c, const bitW amount_is[/*c*/], size_t n, size_t k,
                      bitW A[/*n*/], size_t shift, const bitW& defaultB) const {
    const Logic& L = l_;  // shorthand
    for (size_t i = std::min(n, k + c * shift); i-- > 0;) {
      bitW r = L.bit(0);
      for (size_t j = 0; j < c; ++j) {
        if (i >= j * shift) {
          r = L.lor_exclusive(&r, L.land(&amount_is[j], A[i - j * shift]));
        } else {
          r = L.lor_exclusive(&r, L.land(&amount_is[j], defaultB));
        }
      }
      A[i] = r;
    }
  }

  template <size_t W>
  void really_shift(size_t c, const bitW amount_is[/*c*/], size_t n, size_t k,
                    typename Logic::template bitvec<W> tmp[/*n*/], size_t shift,
                    const typename Logic::template bitvec<W>& defaultA) const {
    const Logic& L = l_;  // shorthand
    for (size_t i = 0; i < n && i < k + shift; ++i) {
      for (size_t w = 0; w < W; ++w) {
        bitW r = L.bit(0);
        for (size_t j = 0; j < c; ++j) {
          if (i + j * shift < n) {
            r = L.lor_exclusive(&r,
                                L.land(&amount_is[j], tmp[i + j * shift][w]));
          } else {
            r = L.lor_exclusive(&r, L.land(&amount_is[j], defaultA[w]));
          }
        }
        tmp[i][w] = r;
      }
    }
  }

  template <size_t W>
  void really_unshift(
      size_t c, const bitW amount_is[/*c*/], size_t n, size_t k,
      typename Logic::template bitvec<W> A[/*n*/], size_t shift,
      const typename Logic::template bitvec<W>& defaultB) const {
    const Logic& L = l_;  // shorthand
    for (size_t i = std::min(n, k + c * shift); i-- > 0;) {
      for (size_t w = 0; w < W; ++w) {
        bitW r = L.bit(0);
        for (size_t j = 0; j < c; ++j) {
          if (i >= j * shift) {
            r = L.lor_exclusive(&r, L.land(&amount_is[j], A[i - j * shift][w]));
          } else {
            r = L.lor_exclusive(&r, L.land(&amount_is[j], defaultB[w]));
          }
        }
        A[i][w] = r;
      }
    }
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_ROUTING_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_SUMCHECK_VERIFIER_LAYERS_H_
#define PRIVACY_PROOFS_ZK_LIB_SUMCHECK_VERIFIER_LAYERS_H_

#include <stddef.h>

#include <cstddef>
#include <memory>

#include "arrays/affine.h"
#include "arrays/dense.h"
#include "arrays/eq.h"
#include "sumcheck/circuit.h"
#include "sumcheck/quad.h"
#include "sumcheck/transcript_sumcheck.h"

namespace proofs {
// Sumcheck verifier that only verifies the layers.
// Derived classes are responsible for verifying the
// input binding, either directly or through a commitment.
template <class Field>
class VerifierLayers {
 public:
  typedef typename Quad<Field>::index_t index_t;
  using Elt = typename Field::Elt;

  struct claims {
    corner_t nv;
    size_t logv;
    Elt claim[2];
    const Elt* q;
    const Elt* g[2];
  };
  // Verify all the circuit layers, returning claims on the inputs in
  // CL.  The caller is responsible to verify the claims, either via
  // direct check or polynomial commitment.
  static bool circuit(const char** why, claims* cl,
                      const Circuit<Field>* CIRCUIT, const Proof<Field>* PROOF,
                      Challenge<Field>* CH, std::unique_ptr<Dense<Field>> V,
                      TranscriptSumcheck<Field>& ts, const Field& F) {
    if (why == nullptr || cl == nullptr || CIRCUIT == nullptr ||
        PROOF == nullptr || CH == nullptr) {
      return false;
    }
    *why = "ok";

    Elt claimV;
    ts.begin_circuit(CH->q, CH->g);

    if (V->n1_ == 1 && V->n0_ == 1 && V->v_[0] == F.zero()) {
      // special case of all-zero binding
      claimV = F.zero();
    } else {
      const desire desires[2] = {
          {V->n1_ == CIRCUIT->nv, "V->n1_ != CIRCUIT->nv"},
          {V->n0_ == CIRCUIT->nc, "V->n0_ != CIRCUIT->nc"},
      };

      if (!check(why, 2, desires)) {
        return false;
      }

      // initial claim on V[G, Q] for the output V
      V->bind_all(CIRCUIT->logc, CH->q, F);
      V->reshape(CIRCUIT->nv);
      V->bind_all(CIRCUIT->logv, CH->g, F);
      claimV = V->scalar();
    }

    // Consider claimV on the binding to P.G as two (identical)
    // claims, so we can get the induction going.  Thus, alpha in
    // the first layer is redundant.
    *cl = claims{
        .nv = CIRCUIT->nv,
        .logv = CIRCUIT->logv,
        .claim = {claimV, claimV},
        .q = CH->q,
        .g = {CH->g, CH->g},
    };

    return layers(why, cl, CIRCUIT, PROOF, ts, CH, F);
  }

  VerifierLayers() = delete;

 private:
  struct desire {
    bool cond;
    const char* why;
  };

  static bool check(const char** why, size_t n, const desire* d) {
    for (size_t i = 0; i < n; ++i) {
      if (!d[i].cond) {
        *why = d[i].why;
        return false;
      }
    }
    return true;
  }

  // Verify CLAIM for one layer and update CLAIM in-place as next
  // claim.  Return TRUE on success, and (FALSE, why) on failure.
  static bool layer_c(const char** why, Elt* claim, size_t logc,
                      const LayerProof<Field>* plr, LayerChallenge<Field>* ch,
                      TranscriptSumcheck<Field>& ts, const Field& F) {
    for (size_t round = 0; round < logc; ++round) {
      // (p(0) + p(1))
      Elt got = F.addf(plr->cp[round].t_[0], plr->cp[round].t_[1]);
      if (got != *claim) {
        *why = "got != claim (round_c)";
        return false;
      }
      ch->cb[round] = ts.round(plr->cp[round]);
      *claim = plr->cp[round].eval_lagrange(ch->cb[round], F);
    }

    return true;
  }

  static bool layer_h(const char** why, Elt* claim, size_t logw,
                      const LayerProof<Field>* plr, LayerChallenge<Field>* ch,
                      TranscriptSumcheck<Field>& ts, const Field& F) {
    for (size_t round = 0; round < logw; ++round) {
      for (size_t hand = 0; hand < 2; ++hand) {
        // (p(0) + p(1))
        Elt got =
            F.addf(plr->hp[hand][round].t_[0], plr->hp[hand][round].t_[1]);
        if (got != *claim) {
          *why = "got != claim (round_h)";
          return false;
        }
        ch->hb[hand][round] = ts.round(plr->hp[hand][round]);
        *claim = plr->hp[hand][round].eval_lagrange(ch->hb[hand][round], F);
      }
    }
    return true;
  }

  // Verify CLAIMS for all layers and update CLAIMS in-place.  Return
  // TRUE on success, and (FALSE, why) on failure.
  static bool layers(const char** why, claims* cl,
                     const Circuit<Field>* CIRCUIT, const Proof<Field>* PROOF,
                     TranscriptSumcheck<Field>& ts, Challenge<Field>* CH,
                     const Field& F) {
    for (size_t ly = 0; ly < CIRCUIT->nl; ++ly) {
      auto clr = &CIRCUIT->l.at(ly);
      auto plr = &PROOF->l[ly];
      auto challenge = &CH->l[ly];

      // the claim is then an affine combination of the two
      // inductive claims
      ts.begin_layer(challenge->alpha, challenge->beta, ly);
      Elt claim = F.addf(cl->claim[0], F.mulf(challenge->alpha, cl->claim[1]));

      if (!layer_c(why, &claim, CIRCUIT->logc, plr, challenge, ts, F)) {
        return false;
      }

      if (!layer_h(why, &claim, clr->logw, plr, challenge, ts, F)) {
        return false;
      }

      // Now verify CLAIM = EQ[Q,C] QUAD[R,L] W[R,C] W[L,C]
      // where W[R,C], W[L,C] are in the proof.

      // bind QUAD[g|r,l] to the alpha-combination of the
      // two G values GR, GL
      auto QUAD = clr->quad->clone();
      QUAD->bind_g(cl->logv, cl->g[0], cl->g[1], challenge->alpha,
                   challenge->beta, F);

      // bind QUAD[G|r,l] to R, L
      for (size_t round = 0; round < clr->logw; ++round) {
        for (size_t hand = 0; hand < 2; ++hand) {
          QUAD->bind_h(challenge->hb[hand][round], hand, F);
        }
      }

      // got = EQ[Q,C] QUAD[G|R,L] W[R,C] W[L,C], where
      // W[.,C] is in the proof.
      Elt got =
          Eq<Field>::eval(CIRCUIT->logc, CIRCUIT->nc, cl->q, challenge->cb, F);
      F.mul(got, QUAD->scalar());
      F.mul(got, plr->wc[0]);
      F.mul(got, plr->wc[1]);

      if (got != claim) {
        *why = "got != claim (layer)";
        return false;
      }

      // Add wc[0,1] to transcript
      ts.write(&plr->wc[0], 1, 2);

      // Reduce to two claims on W[R,C] and W[L,C]
      *cl = claims{
          .nv = clr->nw,
          .logv = clr->logw,
          .claim = {plr->wc[0], plr->wc[1]},
          .q = challenge->cb,
          .g = {challenge->hb[0], challenge->hb[1]},
      };
    }
    return true;
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_SUMCHECK_VERIFIER_LAYERS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_PARAM_H_
#define PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_PARAM_H_

#include <stddef.h>

#include <algorithm>
#include <array>
#include <cstdint>
#include <cstring>
#include <vector>

#include "algebra/blas.h"
#include "merkle/merkle_commitment.h"
#include "merkle/merkle_tree.h"
#include "util/ceildiv.h"
#include "util/crypto.h"
#include "util/panic.h"

/*

  This is an implementation of the Ligero protocol described in

    Ligero: Lightweight Sublinear Arguments
            Without a Trusted Setup,

    Scott Ames and Carmit Hazay and Yuval Ishai and
    Muthuramakrishnan Venkitasubramaniam,
    https://eprint.iacr.org/2022/1608
    doi = {10.1145/3133956},

  The main data structure in the prover is a 2D array which we call a
  tableau organized as follows.

  Fix a block size BLOCK and let DBLOCK = 2 * BLOCK - 1.  Fix another
  quantity BLOCK_EXT >= 0.

  Each row in the tableau has the form [X XD XEXT], where X is a row
  of BLOCK elements, XD is a row of BLOCK - 1 elements, and XEXT is a
  row of BLOCK_EXT elements.  We call the X part the "block" and the
  XEXT part the "extension".

  Let BLOCK_ENC = 2 * BLOCK - 1 + BLOCK_EXT = DBLOCK + BLOCK_EXT be
  the total size of the row.

  A "witness block" has the form [RANDOM[R], WITNESS[W]], where R + W
  = BLOCK.  The randomess (of size R) is used for zero-knowledge
  blinding. Although not strictly required by Ligero, we require W >=
  R to avoid wasting too much space, so that a witness block is at
  least half full.

  A block is interpreted as evaluations of some polynomial at point
  INJ(j) for 0 <= j < BLOCK, where INJ(.) is some field-specific
  injection that injects small natural numbers into distinct field
  elements.  With the condition that the degree of the polynomial be
  less than BLOCK, the polynomial is uniquely determined, and the rest
  [XD XEXT] of the row is then computed as the evaluations of that
  polynomial for BLOCK <= j < BLOCK_ENC.

  To the extent that Ligero is based on Reed-Solomon codes, X is the
  "message" and XEXT is the "codeword". The "rate" is thus BLOCK /
  BLOCK_EXT.

  However, Ligero also needs products of two polynomials of degree
  less than BLOCK, so that the product has degree less than 2 * BLOCK
  - 1 = DBLOCK.  XD exists in the tableau to facilitate the
  computation of these products.  For zero knowledge, the indices of
  XD must be distinct from the indices of BLOCK_EXT.

  We now discuss the row structure of the tableau.  The first three
  rows are special and used for zero-knowledge blinding purposes.

  The first row, row ILDT for ILDT = 0, used for the low-degree test,
  consists of BLOCK random field elements, extended to BLOCK_ENC.

  The second row, row IDOT for IDOT = 1, used in the linear test,
  consists of DBLOCK random field elements, with the additional
  constraint that the double block sum to 0.  As usual, the row is
  extended to BLOCK_ENC by interpolation.

  The third row, row IQUAD for IQUAD = 2, used in the quadratic test,
  consists of DBLOCK random field elements, with the additional
  constraint that the WITNESS portion of the block be zero.  Thus, the
  structure is really [RANDOM[R] ZERO[W] RANDOM[BLOCK-1]], extended to
  BLOCK_ENC by interpolation.

  The next group of "witness rows" IW <= I < IQ for IW = 3, stores
  witnesses.  Each row is a witness block extended to BLOCK_ENC.

  The next group of "quadratic" rows IQ <= I < NROW, has the same
  syntactic structure as the "witness" rows, but they are used in the
  quadratic check in addition to the linear check.  In Ligero, a
  quadratic constraint induces three entries in three quadratic rows.
  Thus, for NQ total quadratic constraints and W useful entries per
  row, we have a total of 3 * (NQ / W) quadratic rows.  To enforce
  this structure, the code stores NQTRIPLES = (NQ / W) instead of the
  number 3 * NQTRIPLES of rows.

 */

namespace proofs {

template <class Field>
struct LigeroParam {
  using Elt = typename Field::Elt;

  // parameters passed by the user
  size_t nw;       // total number of witnesses
  size_t nq;       // total number of quadratic constraints
  size_t rateinv;  // inverse rate of the error-correcting code
  size_t nreq;     // number of opened columns

  // computed parameters
  size_t block_enc;   // total number of elts per row
  size_t block;       // number of elts per block
  size_t dblock;      // 2 * BLOCK - 1
  size_t block_ext;   // BLOCK_ENC - DBLOCK (number of leaves in the
                      // Merkle tree).
  size_t r;           // number of random elts in a witness block
  size_t w;           // number of witnesses in a witness block
  size_t nwrow;       // number of witness rows
  size_t nqtriples;   // number of triples of quadratic-check rows
  size_t nwqrow;      // nwqrow + nqtriples
  size_t nrow;        // total number of rows (nwqrow + three blinding rows)
  size_t mc_pathlen;  // length of a Merkle-tree proof
                      // with BLOCK_ENC-BLOCK leaves

  // layout of rows
  size_t ildt;   // blinding for the low-degree test
  size_t idot;   // blinding row for the dot-product check
  size_t iquad;  // blinding row for the quadratic check
  size_t iw;     // first witness row
  size_t iq;     // first quadratic row

  LigeroParam(size_t nw, size_t nq, size_t rateinv, size_t nreq)
      : nw(nw), nq(nq), rateinv(rateinv), nreq(nreq) {
    r = nreq;

    size_t min_proof_size = SIZE_MAX;
    size_t best_block_enc = 1;
    for (size_t e = 1; e <= (1 << 28); e *= 2) {
      size_t proof_size = layout(e);
      if (proof_size < min_proof_size) {
        min_proof_size = proof_size;
        best_block_enc = e;
      }
    }

    // recompute parameters
    layout(best_block_enc);
    proofs::check(block_enc > block, "block_enc > block");

    ildt = 0;
    idot = 1;
    iquad = 2;
    iw = 3;
    iq = iw + nwrow;
    proofs::check(nrow == iq + 3 * nqtriples, "nrow == iq + 3 * nqtriples");
  }

 private:
  // Return an estimate of the proof size.
  //
  // This function is kind of a hack in that it breaks abstraction
  // boundaries, e.g. it knows about the size and layout of the Merkle
  // commitment.  Punt on this wart until we have a better theory.
  size_t layout(size_t e) {
    // Maximum size we are prepared to handle.  All dimensions will be
    // required to be < MAX_SIZE.  In principle we could handle all
    // size_t, but we want 64-bit code to fail if it would fail on a
    // 32-bit machine, and for maximum paranoia we restrict to 28
    // bits, since one cannot malloc 2^{28} Elts on a 32-bit machine
    // anyway.
    constexpr size_t max_lg_size = 28;
    constexpr size_t max_size = static_cast<size_t>(1) << max_lg_size;
    block_enc = e;

    // block_enc must fit in the subfield
    size_t subfield_bits = 8 * Field::kSubFieldBytes;
    if (subfield_bits <= max_lg_size) {
      if (block_enc >= (static_cast<size_t>(1) << subfield_bits)) {
        return SIZE_MAX;
      }
    }

    // limit block_enc to avoid overflow in the computation
    // of the proof size
    if (block_enc > max_size || rateinv > max_size ||
        (block_enc + 1) < (2 + rateinv)) {
      return SIZE_MAX;
    }

    block = (block_enc + 1) / (2 + rateinv);
    // now 1 <= BLOCK < MAX_SIZE / 2

    // Ensure BLOCK = R + W (syntactic property)
    if (block < r) {
      return SIZE_MAX;
    }
    w = block - r;

    // now r <= BLOCK < MAX_SIZE / 2
    //     0 <= W < MAX_SIZE / 2
    //     0 <= W <= BLOCK
    //     0 <= R <= BLOCK
    //     W + R == BLOCK

    // Ensure W >= R (needed for reasonable space utilization).
    if (w < r) {
      return SIZE_MAX;
    }
    // now R <= W < MAX_SIZE

    // Finish the layout of a row
    dblock = 2 * block - 1;
    // now DBLOCK < MAX_SIZE

    // Ensure BLOCK_ENC >= 0 (syntactic property).  Should be true
    // for any reasonable rateinv, but check anyway.
    if (block_enc < dblock) {
      return SIZE_MAX;
    }
    // now DBLOCK <= BLOCK_ENC

    block_ext = block_enc - dblock;
    // now 0 <= BLOCK_EXT < MAX_SIZE

    nwrow = ceildiv(nw, w);
    nqtriples = ceildiv(nq, w);

    nwqrow = nwrow + 3 * nqtriples;
    nrow = nwqrow + /*blinding rows=*/3;

    // The total number of elements (NROW * BLOCK_ENC) in the tableau
    // must fit in MAX_SIZE.
    if (nrow >= max_size / block_enc) {
      return SIZE_MAX;
    }

    mc_pathlen = merkle_commitment_len(block_ext);

    /* proof+commitment size.  */
    // Compute the size in uint64_t instead of size_t since
    // I am too lazy to worry about overflow.
    uint64_t sz = 0;

    // commitment
    sz += sizeof(Digest);

    // Merkle openings, approximated because the exact # of leaves depends
    // on the random coins.
    sz += static_cast<uint64_t>(mc_pathlen) / 2 * static_cast<uint64_t>(nreq) *
          static_cast<uint64_t>(Digest::kLength);

    // y_ldt
    sz += static_cast<uint64_t>(block) * static_cast<uint64_t>(Field::kBytes);

    // y_dot
    sz += static_cast<uint64_t>(dblock) * static_cast<uint64_t>(Field::kBytes);

    // y_quad
    // The quadratic-test response has size DBLOCK, but W elements
    // are expected to be zero and not serialized.
    sz += static_cast<uint64_t>(dblock - w) *
          static_cast<uint64_t>(Field::kBytes);

    // nonces
    sz += static_cast<uint64_t>(nreq) *
          static_cast<uint64_t>(MerkleNonce::kLength);

    // req.   Assume optimistically that all elements are in the subfield.
    sz += static_cast<uint64_t>(nrow) * static_cast<uint64_t>(nreq) *
          static_cast<uint64_t>(Field::kSubFieldBytes);

    sz = std::min<uint64_t>(sz, SIZE_MAX);
    return static_cast<size_t>(sz);
  }
};

template <class Field>
struct LigeroCommitment {
  Digest root;
};

template <class Field>
struct LigeroProof {
  using Elt = typename Field::Elt;
  explicit LigeroProof(const LigeroParam<Field> *p)
      : block(p->block),
        dblock(p->dblock),
        r(p->r),
        block_enc(p->block_enc),
        nrow(p->nrow),
        nreq(p->nreq),
        mc_pathlen(p->mc_pathlen),
        y_ldt(p->block),
        y_dot(p->dblock),
        y_quad_0(p->r),
        y_quad_2(p->dblock - p->block),
        req(p->nrow * p->nreq),
        merkle(p->nreq) {}

  // The proof stores a copy of all parameters relevant to the proof.
  size_t block;
  size_t dblock;
  size_t r;
  size_t block_enc;
  size_t nrow;
  size_t nreq;
  size_t mc_pathlen;

  std::vector<Elt> y_ldt;     // [block]
  std::vector<Elt> y_dot;     // [dblock]
  std::vector<Elt> y_quad_0;  // [r] first part of y_quad.
  // The middle part [w] of y_quad is zero and not transmitted.
  std::vector<Elt> y_quad_2;  // [dblock - block] last part of y_quad
  std::vector<Elt> req;       // [nrow, nreq]
  MerkleProof merkle;

  Elt &req_at(size_t i, size_t j) { return req[i * nreq + j]; }
  const Elt &req_at(size_t i, size_t j) const { return req[i * nreq + j]; }
};

// a nonzero entry in the matrix A that defines
// the linear constraints A w = b.  The term
// states that A[c, w] = k, where the "row"
// c is interpreted as the constraint index, and
// the "column" w is interpreted as the witness
// index
template <class Field>
struct LigeroLinearConstraint {
  using Elt = typename Field::Elt;
  size_t c;
  size_t w;
  Elt k;
};

// encode W[X] * W[Y] - W[Z] = 0
struct LigeroQuadraticConstraint {
  size_t x;
  size_t y;
  size_t z;
};

template <class Field>
class LigeroCommon {
  using Elt = typename Field::Elt;

 public:
  // create a grand dot product by A given the user-provided
  // linear-constraint terms LLTERM, the quadratic constraints LQC,
  // and their random challenges ALPHAL, ALPHAQ.
  static void inner_product_vector(
      Elt A[/*nwqrow, w*/], const LigeroParam<Field> &p, size_t nl,
      size_t nllterm, const LigeroLinearConstraint<Field> llterm[/*nllterm*/],
      const Elt alphal[/*nl*/], const LigeroQuadraticConstraint lqc[/*nq*/],
      const std::array<Elt, 3> alphaq[/*nq*/], const Field &F) {
    // clear A and overwrite it later.
    Blas<Field>::clear(p.nwqrow * p.w, A, 1, F);

    // random linear combinations of the linear constraints
    for (size_t l = 0; l < nllterm; ++l) {
      const auto &term = llterm[l];
      proofs::check(term.w < p.nw, "term.w < p.nw");
      proofs::check(term.c < nl, "term.c < nl");
      F.add(A[term.w], F.mulf(term.k, alphal[term.c]));
    }

    // routing terms for quadratic constraints
    Elt *Ax = &A[p.nwrow * p.w];
    Elt *Ay = Ax + (p.nqtriples * p.w);
    Elt *Az = Ay + (p.nqtriples * p.w);

    for (size_t i = 0; i < p.nqtriples; ++i) {
      for (size_t j = 0; j < p.w && j + i * p.w < p.nq; ++j) {
        // index into [_ , W] arrays
        size_t iw = j + i * p.w;
        const auto *l = &lqc[iw];
        F.add(Ax[iw], alphaq[iw][0]);
        F.sub(A[l->x], alphaq[iw][0]);

        F.add(Ay[iw], alphaq[iw][1]);
        F.sub(A[l->y], alphaq[iw][1]);

        F.add(Az[iw], alphaq[iw][2]);
        F.sub(A[l->z], alphaq[iw][2]);
      }
    }
  }

  // layout a witness block where the "witness" is public, and
  // thus the randomess is zero.
  static void layout_Aext(Elt Aext[/*>=block*/], const LigeroParam<Field> &p,
                          size_t i, const Elt A[/*nwqrow, nw*/],
                          const Field &F) {
    Blas<Field>::clear(p.r, &Aext[0], 1, F);
    Blas<Field>::copy(p.w, &Aext[p.r], 1, &A[i * p.w], 1);
  }

  static void column_hash(size_t n, const Elt x[/*n:incx*/], size_t incx,
                          SHA256 &sha, const Field &F) {
    for (size_t i = 0; i < n; ++i) {
      uint8_t buf[Field::kBytes];
      F.to_bytes_field(buf, x[i * incx]);
      sha.Update(buf, sizeof(buf));
    }
  }
};

// A struct representing the hash of llterms.  It is really the
// same as Digest, but in theory Ligero should exist independently
// of the Merkle tree.
struct LigeroHash {
  static constexpr size_t kLength = kSHA256DigestSize;
  uint8_t bytes[kLength];
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_PARAM_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_SUMCHECK_TRANSCRIPT_SUMCHECK_H_
#define PRIVACY_PROOFS_ZK_LIB_SUMCHECK_TRANSCRIPT_SUMCHECK_H_

#include <stddef.h>

#include "arrays/affine.h"
#include "arrays/dense.h"
#include "random/transcript.h"
#include "sumcheck/circuit.h"

namespace proofs {
/*
Fiat-Shamir abstraction for sumcheck protocol.
This class takes wraps a transcript object and provides the interface for
sumcheck challenge and response.
*/
template <typename Field>
class TranscriptSumcheck {
  using Elt = typename Field::Elt;
  using CPoly = typename Proof<Field>::CPoly;
  using WPoly = typename Proof<Field>::WPoly;
  static constexpr size_t kMaxBindings = Proof<Field>::kMaxBindings;

 public:
  explicit TranscriptSumcheck(Transcript& ts, const Field& F)
      : ts_(ts), f_(F) {}

  void write_input(const Dense<Field>* X) {
    // Write column by column to make it compatible with oracle.
    for (corner_t c = 0; c < X->n0_; ++c) {
      ts_.write(&X->v_[c], X->n0_, X->n1_, f_);
    }
  }

  void begin_circuit(Elt* Q, Elt* G) {
    ts_.elt(Q, kMaxBindings, f_);
    ts_.elt(G, kMaxBindings, f_);
  }

  void begin_layer(Elt& alpha, Elt& beta, size_t layer) {
    alpha = ts_.elt(f_);
    beta = ts_.elt(f_);
  }

  void write(const Elt e[/*n*/], size_t ince, size_t n) {
    ts_.write(e, ince, n, f_);
  }

  template <class Poly>
  Elt /*R*/ round(const Poly& poly) {
    write_poly(&poly);
    return ts_.elt(f_);
  }

 private:
  template <class Poly>
  void write_poly(const Poly* poly) {
    // Do not write the p(1) value to the transcript, as its value is
    // implied by the constraints, and we can omit it from the proof.
    for (size_t i = 0; i < Poly::kN; ++i) {
      if (i != 1) {
        ts_.write(poly->t_[i], f_);
      }
    }
  }
  Transcript& ts_;
  const Field& f_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_SUMCHECK_TRANSCRIPT_SUMCHECK_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_WITNESS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_WITNESS_H_

#include <cstddef>
#include <cstdint>

#include "arrays/dense.h"
#include "circuits/logic/bit_plucker_encoder.h"
#include "gf2k/gf2_128.h"

namespace proofs {

template <class Field>
class MacWitness {
  using f_128 = GF2_128<>;
  using gf2k = f_128::Elt;
  using packer = BitPluckerEncoder<Field, 2>;
  using packed_v128 = typename packer::packed_v128;
  using packed_v256 = typename packer::packed_v256;

 public:
  explicit MacWitness(const Field& F, const f_128& GF) : f_(F), gf_(GF) {}

  void fill_witness(DenseFiller<Field>& fill) const {
    packer bp(f_);
    uint8_t tmp[f_128::kBits];
    for (size_t i = 0; i < 2; ++i) {
      for (size_t j = 0; j < f_128::kBits; ++j) {
        tmp[j] = ap_[i][j];
      }
      fill.push_back(bp.template pack<packed_v128>(tmp, f_128::kBits));
    }

    for (size_t i = 0; i < 2; ++i) {
      for (size_t j = 0; j < f_128::kBits; ++j) {
        tmp[j] = x_[i][j];
      }
      fill.push_back(bp.template pack<packed_v128>(tmp, 128));
    }
  }

  // Computes a mac witness on a 32-byte message x.
  // This code assumes that a gf element is at least 16 bytes.
  void compute_witness(const gf2k a_p[/*2*/], const uint8_t x[/*32*/]) {
    for (size_t i = 0; i < 2; ++i) {
      x_[i] = gf_.of_bytes_field(&x[i * 16]).value();
      ap_[i] = a_p[i];
    }
  }

 private:
  gf2k ap_[2], x_[2];
  const Field& f_;
  const f_128& gf_;
};

class MacGF2Witness {
  using f_128 = GF2_128<>;
  using gf2k = f_128::Elt;

 public:
  void fill_witness(DenseFiller<f_128>& fill) const {
    fill.push_back(ap_[0]);
    fill.push_back(ap_[1]);
  }

  // Computes a mac witness on a 32-byte message x.
  void compute_witness(const gf2k a_p[/*2*/]) {
    for (size_t i = 0; i < 2; ++i) {
      ap_[i] = a_p[i];
    }
  }

 private:
  gf2k ap_[2];
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_WITNESS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_GF2K_LCH14_REED_SOLOMON_H_
#define PRIVACY_PROOFS_ZK_LIB_GF2K_LCH14_REED_SOLOMON_H_

#include <stdio.h>

#include <algorithm>
#include <memory>
#include <vector>

#include "gf2k/lch14.h"

namespace proofs {

template <class Field>
class LCH14ReedSolomon {
  using Elt = typename Field::Elt;

  // only works in binary fields
  static_assert(Field::kCharacteristicTwo);

 public:
  // We interpolate N points, assumed to be the evaluations at
  // F.of_scalar(i), 0 <= i < N, of a polynomial of degree <N, to M
  // points 0 <= i < M.  (Thus, the M points include the N points
  // we started with.)
  //
  // In principle we don't need to know N and M at construction time,
  // but we require N and M for compatibility of the interface with
  // the ReedSolomon class over prime fields.
  LCH14ReedSolomon(size_t n, size_t m, const Field& F)
      : f_(F), n_(n), m_(m), fft_(F) {}

  // Y[i] is expected to be defined for 0 <= i < N, and this
  // routine fills it for 0 <= i < M
  void interpolate(Elt y[/*m*/]) const {
    // determine the FFT size
    size_t l = 0;
    size_t fftn = 1;
    while (fftn < n_) {
      fftn <<= 1;
      ++l;
    }

    // "coefficients" in the LCH14 novel polynomial basis
    std::vector<Elt> C(fftn);

    // compute the "coefficients" under the assumption
    // that we know n_ evaluations and that the higher-order
    // (fftn - n_) "coefficients" are zero.
    for (size_t i = 0; i < n_; ++i) {
      C[i] = y[i];
    }
    for (size_t i = n_; i < fftn; ++i) {
      C[i] = f_.zero();
    }
    fft_.BidirectionalFFT(l, /*k=*/n_, &C[0]);

    // fill in the missing evaluations in the first coset, since we
    // already have the missing evaluations in C[[n_, (1<<l))]
    for (size_t i = n_; i < std::min(m_, fftn); ++i) {
      y[i] = C[i];
    }

    // revert C to pure coefficients for later use
    for (size_t i = n_; i < fftn; ++i) {
      C[i] = f_.zero();
    }

    // all remaining cosets:
    for (size_t coset = 1; (coset << l) < m_; ++coset) {
      size_t b = (coset << l);
      if (b + fftn <= m_) {
        // if the coset fits completely within Y[],
        // copy the coefficients into Y and transform in place
        for (size_t i = 0; i < fftn; ++i) {
          y[i + b] = C[i];
        }
        fft_.FFT(l, b, &y[b]);
      } else {
        // Partial fit.  Transform C and copy the output.
        fft_.FFT(l, b, &C[0]);
        for (size_t i = 0; i + b < m_; ++i) {
          y[i + b] = C[i];
        }
        // Now we have destroyed C, but this is ok because
        // this is the last iteration
      }
    }
  }

 private:
  const Field& f_;
  size_t n_;
  size_t m_;
  LCH14<Field> fft_;
};

template <class Field>
class LCH14ReedSolomonFactory {
 public:
  explicit LCH14ReedSolomonFactory(const Field& f) : f_(f) {}

  std::unique_ptr<LCH14ReedSolomon<Field>> make(size_t n, size_t m) const {
    return std::make_unique<LCH14ReedSolomon<Field>>(n, m, f_);
  }

 private:
  const Field& f_;
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_GF2K_LCH14_REED_SOLOMON_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_CONVOLUTION_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_CONVOLUTION_H_

#include <stddef.h>

#include <cstdint>
#include <memory>
#include <vector>

#include "algebra/blas.h"
#include "algebra/fft.h"
#include "algebra/rfft.h"

/*
All of the classes in this package compute convolutions.
That is, given inputs arrays of field elements x, y, with |x|=n, |y|=m,
these methods compute the first m entries of

   z[k] = \sum_{i=0}^{n-1} x[i] y[k-i]

SlowConvolution uses an O(n*m) method for testing validation.

FFTConvolution and FFTExtConvolution first pad y to length n and use advanced
FFT algorithms to compute the same in O(nlogn) time.

The const Field& objects that are passed have lifetimes that exceed the call
durations and can be safely passed by const reference.
*/

namespace proofs {

// Returns the smallest power of 2 that is at least n.
static size_t choose_padding(const size_t n) {
  size_t p = 1;
  while (p < n) {
    p *= 2;
  }
  return p;
}

template <class Field>
class FFTConvolution {
  using Elt = typename Field::Elt;

 public:
  FFTConvolution(size_t n, size_t m, const Field& f, const Elt omega,
                 uint64_t omega_order, const Elt y[/*m*/])
      : f_(f),
        omega_(omega),
        omega_order_(omega_order),
        n_(n),
        m_(m),
        padding_(choose_padding(m)),
        y_fft_(padding_, f_.zero()) {
    Blas<Field>::copy(m, &y_fft_[0], 1, y, 1);
    FFT<Field>::fftf(&y_fft_[0], padding_, omega_, omega_order_, f_);

    // Pre-scale Y by 1/N to compensate for the scaling in FFTB(FFTF(.))
    Blas<Field>::scale(padding_, &y_fft_[0], 1,
                       f_.invertf(f_.of_scalar(padding_)), f_);
  }

  // Computes (first m entries of) convolution of x with y, outputs in z:
  // z[k] = \sum_{i=0}^{n-1} x[i] y[k-i].
  // Note that y has already been FFT'd and divided by padding_ in constructor
  void convolution(const Elt x[/*n_*/], Elt z[/*m_*/]) const {
    std::vector<Elt> x_fft(padding_, f_.zero());
    Blas<Field>::copy(n_, &x_fft[0], 1, x, 1);
    FFT<Field>::fftf(&x_fft[0], padding_, omega_, omega_order_, f_);
    // Pointwise multiplication.
    for (size_t i = 0; i < padding_; ++i) {
      f_.mul(x_fft[i], y_fft_[i]);
    }
    // Backward fft.
    FFT<Field>::fftb(&x_fft[0], padding_, omega_, omega_order_, f_);
    Blas<Field>::copy(m_, z, 1, &x_fft[0], 1);
  }

 private:
  const Field& f_;
  const Elt omega_;
  const uint64_t omega_order_;

  // n is the number of points input
  size_t n_;
  size_t m_;  // total number of points output (points in + new points out)
  size_t padding_;

  // fft(y[i]) / padding
  // padded with zeroes to the next power of 2 at least m.
  std::vector<Elt> y_fft_;
};

template <class Field>
class FFTConvolutionFactory {
  using Elt = typename Field::Elt;

 public:
  using Convolver = FFTConvolution<Field>;
  FFTConvolutionFactory(const Field& f, const Elt omega, uint64_t omega_order)
      : f_(f), omega_(omega), omega_order_(omega_order) {}

  std::unique_ptr<const Convolver> make(size_t n, size_t m,
                                        const Elt y[/*m*/]) const {
    return std::make_unique<const Convolver>(n, m, f_, omega_, omega_order_, y);
  }

 private:
  const Field& f_;
  const Elt omega_;
  const uint64_t omega_order_;
};

template <class Field, class FieldExt>
class FFTExtConvolution {
  using Elt = typename Field::Elt;
  using EltExt = typename FieldExt::Elt;

 public:
  FFTExtConvolution(size_t n, size_t m, const Field& f, const FieldExt& f_ext,
                    const EltExt omega, uint64_t omega_order,
                    const Elt y[/*m*/])
      : f_(f),
        f_ext_(f_ext),
        omega_(omega),
        omega_order_(omega_order),
        n_(n),
        m_(m),
        padding_(choose_padding(m)),
        y_fft_(padding_, f_.zero()) {
    Blas<Field>::copy(m, &y_fft_[0], 1, y, 1);
    RFFT<FieldExt>::r2hc(&y_fft_[0], padding_, omega_, omega_order_, f_ext_);

    // Pre-scale Y by 1/N to compensate for the scaling in HC2R(R2HC(.))
    Blas<Field>::scale(padding_, &y_fft_[0], 1,
                       f_.invertf(f_.of_scalar(padding_)), f_);
  }

  // Computes (first m entries of) convolution of x with y, stores in z:
  // z[k] = \sum_{i=0}^{n-1} x[i] y[k-i].
  // Note that y has already been FFT'd and divided by padding_ in constructor
  void convolution(const Elt x[/*n_*/], Elt z[/*m_*/]) const {
    std::vector<Elt> x_fft(padding_, f_.zero());
    Blas<Field>::copy(n_, &x_fft[0], 1, x, 1);
    RFFT<FieldExt>::r2hc(&x_fft[0], padding_, omega_, omega_order_, f_ext_);

    // Pointwise multiplication
    {
      size_t i;
      f_.mul(x_fft[0], y_fft_[0]);  // DC is real
      for (i = 1; i + i < padding_; ++i) {
        RFFT<FieldExt>::cmul(&x_fft[i], &x_fft[padding_ - i], x_fft[i],
                             x_fft[padding_ - i], y_fft_[i],
                             y_fft_[padding_ - i], f_);
      }
      f_.mul(x_fft[i], y_fft_[i]);  // Nyquist is real
    }

    // Backward FFT.
    RFFT<FieldExt>::hc2r(&x_fft[0], padding_, omega_, omega_order_, f_ext_);
    Blas<Field>::copy(m_, z, 1, &x_fft[0], 1);
  }

 private:
  const Field& f_;
  const FieldExt& f_ext_;
  const EltExt omega_;
  const uint64_t omega_order_;

  // n is the number of points input in x
  size_t n_;
  size_t m_;  // total number of points output in convolution
  size_t padding_;

  // fft(y[i]) / padding
  // padded with zeroes to the next power of 2 at least m.
  std::vector<Elt> y_fft_;
};

template <class Field, class FieldExt>
class FFTExtConvolutionFactory {
  using Elt = typename Field::Elt;
  using EltExt = typename FieldExt::Elt;

 public:
  using Convolver = FFTExtConvolution<Field, FieldExt>;

  FFTExtConvolutionFactory(const Field& f, const FieldExt& f_ext,
                           const EltExt omega, uint64_t omega_order)
      : f_(f), f_ext_(f_ext), omega_(omega), omega_order_(omega_order) {}

  std::unique_ptr<const Convolver> make(size_t n, size_t m,
                                        const Elt y[/*m*/]) const {
    return std::make_unique<const Convolver>(n, m, f_, f_ext_, omega_,
                                             omega_order_, y);
  }

 private:
  const Field& f_;
  const FieldExt& f_ext_;
  const EltExt omega_;
  const uint64_t omega_order_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_CONVOLUTION_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_MERKLE_MERKLE_COMMITMENT_H_
#define PRIVACY_PROOFS_ZK_LIB_MERKLE_MERKLE_COMMITMENT_H_

#include <stddef.h>
#include <stdint.h>

#include <cstring>
#include <functional>
#include <vector>

#include "merkle/merkle_tree.h"
#include "random/random.h"
#include "util/crypto.h"

namespace proofs {

struct MerkleNonce {
  static constexpr size_t kLength = kSHA256DigestSize;
  uint8_t bytes[kLength];
};

struct MerkleProof {
  explicit MerkleProof(size_t nreq) : nonce(nreq), path() {}

  std::vector<MerkleNonce> nonce;  // [nreq]
  std::vector<Digest> path;        // variable size, but < nreq * mt_pathlen
};

inline size_t merkle_commitment_len(size_t n) { return merkle_tree_len(n); }

// prover-side
class MerkleCommitment {
 public:
  explicit MerkleCommitment(size_t n) : n_(n), mt_(n), nonce_(n) {}

  Digest commit(const std::function<void(size_t, SHA256 &)> &updhash,
                RandomEngine &rng) {
    for (size_t i = 0; i < n_; ++i) {
      SHA256 sha;
      rng.bytes(nonce_[i].bytes, MerkleNonce::kLength);
      sha.Update(nonce_[i].bytes, MerkleNonce::kLength);
      updhash(i, sha);

      Digest dig;
      sha.DigestData(dig.data);
      mt_.set_leaf(i, dig);
    }

    return mt_.build_tree();
  }

  void open(MerkleProof &proof, const size_t pos[/*np*/], size_t np) {
    // fill in the nonces of the opening
    for (size_t i = 0; i < np; ++i) {
      proof.nonce[i] = nonce_[pos[i]];
    }

    (void)mt_.generate_compressed_proof(proof.path, pos, np);
  }

 private:
  size_t n_;
  MerkleTree mt_;
  std::vector<MerkleNonce> nonce_;
};

// Declare a class for symmetry, but this class is never instantiated
class MerkleCommitmentVerifier {
 public:
  static bool verify(size_t n, const Digest &root, const MerkleProof &proof,
                     const size_t pos[/*nreq*/], size_t nreq,
                     const std::function<void(size_t, SHA256 &)> &updhash) {
    // Assemble the expected leaf values
    std::vector<Digest> leaves(nreq);
    for (size_t r = 0; r < nreq; ++r) {
      SHA256 sha;
      sha.Update(proof.nonce[r].bytes, MerkleNonce::kLength);
      updhash(r, sha);
      sha.DigestData(leaves[r].data);
    }

    MerkleTreeVerifier mtv(n, root);
    return mtv.verify_compressed_proof(proof.path.data(), proof.path.size(),
                                       &leaves[0], pos, nreq);
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_MERKLE_MERKLE_COMMITMENT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_RANDOM_TRANSCRIPT_H_
#define PRIVACY_PROOFS_ZK_LIB_RANDOM_TRANSCRIPT_H_

#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>

#include "random/random.h"
#include "util/crypto.h"
#include "util/panic.h"
#include "util/serialization.h"

namespace proofs {

/*
FSPRF and Transcript together used implement the Fiat-Shamir transform.
*/
class FSPRF  {
 public:
  explicit FSPRF(const uint8_t key[kPRFKeySize])
      : prf_(key), nblock_(0), rdptr_(kPRFOutputSize) {}

  // Disable copy for good measure.
  explicit FSPRF(const FSPRF&) = delete;
  FSPRF& operator=(const FSPRF&) = delete;

  void bytes(uint8_t buf[/*n*/], size_t n) {
    while (n-- > 0) {
      if (rdptr_ == kPRFOutputSize) {
        refill();
      }
      *buf++ = saved_[rdptr_++];
    }
  }

 private:
  void refill() {
    uint8_t in[kPRFInputSize] = {};
    u64_to_le(in, nblock_++);
    prf_.Eval(saved_, in);
    rdptr_ = 0;
  }

  PRF prf_;
  uint64_t nblock_;
  size_t rdptr_;       // read pointer into saved[]
  uint8_t saved_[kPRFOutputSize];  // saved pseudo-random bytes
};

class Transcript : public RandomEngine {
  enum { TAG_BSTR = 0, TAG_FIELD_ELEM = 1, TAG_ARRAY = 1 };

 public:
  // A transcript must be explicitly initialized so that each instance of
  // the Random oracle is unique.
  Transcript(const uint8_t init[], size_t init_len) : sha_(), prf_() {
    write(init, init_len);
  }

  // Remove default copy and move implementations.
  Transcript(const Transcript&) = delete;
  Transcript& operator=(const Transcript&) = delete;

  // Explicit copy to avoid accidental passing by value.
  Transcript clone() { return Transcript(sha_); }

  // Generate bytes by via the current FSPRF object.
  void bytes(uint8_t buf[/*n*/], size_t n) override {
    if (!prf_) {
      uint8_t key[kPRFKeySize];
      get(key);
      prf_ = std::make_unique<FSPRF>(key);
    }
    prf_->bytes(buf, n);
  }

  // snapshot the hash of the transcript so far
  void get(uint8_t key[/*kPRFKeySize*/]) {
    check(kPRFKeySize == kSHA256DigestSize, "prf key size != digest output");
    // fork the state because we will finalize it
    SHA256 tmp_hash;
    tmp_hash.CopyState(sha_);
    tmp_hash.DigestData(key);
  }

  // Typed write operations.  We tag byte-array(n), field-element, and
  // array-of-field-element(n).
  //
  // We make a few arbitrary choices that make no real difference.
  // All lengths are 64-bit.  We distinguish a field element from
  // an array of one field element, which is kind of arbitrary.

  // byte string
  void write(const uint8_t data[/*n*/], size_t n) {
    tag(TAG_BSTR);
    length(n);

    write_untyped(data, n);
  }

  // N zero bytes
  void write0(size_t n) {
    tag(TAG_BSTR);
    length(n);

    uint8_t data[32] = {};
    for (; n > 32; n -= 32) {
      write_untyped(data, 32);
    }
    write_untyped(data, n);
  }

  // one field element
  template <class Field>
  void write(const typename Field::Elt& e, const Field& F) {
    tag(TAG_FIELD_ELEM);

    write_untyped(e, F);
  }

  // array of field elements
  template <class Field>
  void write(const typename Field::Elt e[/*n*/], size_t ince, size_t n,
             const Field& F) {
    tag(TAG_ARRAY);
    length(n);

    for (size_t i = 0; i < n; ++i) {
      write_untyped(e[i * ince], F);
    }
  }

 private:
  explicit Transcript(const SHA256& sha) : sha_() {
    sha_.CopyState(sha);
  }

  // Output a 1-byte tag
  void tag(size_t t) {
    uint8_t d = static_cast<uint8_t>(t);
    write_untyped(&d, 1);
  }

  // Output a 8-byte length.  We pass the length
  // as size_t, but we always write it as uint64_t
  void length(size_t x) {
    uint8_t a[8];
    u64_to_le(a, x);
    write_untyped(a, 8);
  }

  void write_untyped(const uint8_t data[/*n*/], size_t n) {
    // invalidate the PRF on any writes
    prf_.reset();
    sha_.Update(data, n);
  }

  template <class Field>
  void write_untyped(const typename Field::Elt& e, const Field& F) {
    uint8_t buf[Field::kBytes];
    F.to_bytes_field(buf, e);
    write_untyped(buf, sizeof(buf));
  }

  SHA256 sha_;
  std::unique_ptr<FSPRF> prf_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_RANDOM_TRANSCRIPT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_MEMCMP_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_MEMCMP_H_

#include <stddef.h>

#include <vector>

namespace proofs {
// This class implements the an equivalent of memcmp for arrays of
// v8.  The logic comparison operators do all the work, and the only
// problem is to arrange bits in the correct order for comparison.
// In more detail, these methods compare the bit strings represented by
// the array of v8 inputs (recall a v8 is 8 wires each containing a {0,1} value
// in the Field).
template <class Logic>
class Memcmp {
 public:
  using BitW = typename Logic::BitW;
  using v8 = typename Logic::v8;
  const Logic& l_;

  explicit Memcmp(const Logic& l) : l_(l) {}

  // A < B
  BitW lt(size_t n, const v8 A[/*n*/], const v8 B[/*n*/]) const {
    std::vector<BitW> a(8 * n);
    std::vector<BitW> b(8 * n);
    arrange(n, a.data(), A);
    arrange(n, b.data(), B);
    return l_.lt(8 * n, a.data(), b.data());
  }

  // A <= B
  BitW leq(size_t n, const v8 A[/*n*/], const v8 B[/*n*/]) const {
    std::vector<BitW> a(8 * n);
    std::vector<BitW> b(8 * n);
    arrange(n, a.data(), A);
    arrange(n, b.data(), B);
    return l_.leq(8 * n, a.data(), b.data());
  }

 private:
  void arrange(size_t n, BitW bits[/* 8 * n */], const v8 bytes[/*n*/]) const {
    // from LSB to MSB:
    for (size_t i = n; i-- > 0;) {
      for (size_t j = 0; j < 8; ++j) {
        *bits++ = bytes[i][j];
      }
    }
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_MEMCMP_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_COMPILER_BACKEND_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_COMPILER_BACKEND_H_

#include <stdlib.h>

#include <cstddef>

#include "circuits/compiler/compiler.h"

namespace proofs {
// backend that compiles a circuit that, when evaluated, computes Elt's
template <class Field>
class CompilerBackend {
  using QuadCircuitF = QuadCircuit<Field>;
  using Elt = typename Field::Elt;

 public:
  using V = size_t;

  explicit CompilerBackend(QuadCircuitF* q) : q_(q) {}

  V assert0(const V& a) const { return q_->assert0(a); }
  V add(const V& a, const V& b) const { return q_->add(a, b); }
  V sub(const V& a, const V& b) const {
    auto mb = mul(konst(q_->f_.mone()), b);
    return add(a, mb);
  }
  V mul(const V& a, const V& b) const { return q_->mul(a, b); }
  V mul(const Elt& a, const V& b) const { return q_->mul(a, b); }
  V mul(const Elt& a, const V& b, const V& c) const { return q_->mul(a, b, c); }
  V konst(const Elt& a) const { return q_->konst(a); }

  V ax(const Elt& a, const V& x) const { return q_->mul(a, x); }
  V axy(const Elt& a, const V& x, const V& y) const { return q_->mul(a, x, y); }
  V axpy(const V& y, const Elt& a, const V& x) const {
    return q_->axpy(y, a, x);
  }
  V apy(const V& y, const Elt& a) const { return q_->apy(y, a); }

  V input() const { return q_->input(); }
  void output(size_t n, V wire_id) const { q_->output(n, wire_id); }
  size_t wire_id(const V& a) const { return q_->wire_id(a); }

 private:
  QuadCircuitF* q_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_COMPILER_BACKEND_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_SUMCHECK_CIRCUIT_H_
#define PRIVACY_PROOFS_ZK_LIB_SUMCHECK_CIRCUIT_H_

#include <stddef.h>

#include <cstdint>
#include <memory>
#include <vector>

#include "algebra/poly.h"
#include "arrays/affine.h"
#include "sumcheck/quad.h"

namespace proofs {
template <class Field>
struct Layer {
  corner_t nw;  // number of inputs
  size_t logw;  // number of binding rounds for the hand variables
  std::unique_ptr<const Quad<Field>> quad;

  bool operator==(const Layer& y) const {
    // This operator relies on the layer being properly constructed, so that
    // the quad reference is never a nullptr.
    return nw == y.nw && logw == y.logw && *quad == *y.quad;
  }

  size_t nterms() const { return quad->n_; }
};

template <class Field>
struct Circuit {
  corner_t nv;  // number of outputs for one copy
  size_t logv;  // number of G variables in V[G,C] in the final output
  corner_t nc;  // number of copies
  size_t logc;  // number of sumcheck rounds for the C variables
  size_t nl;    // number of layers

  size_t ninputs;  //  number of inputs
  size_t npub_in;  //  number of public inputs, index of first private input
  size_t subfield_boundary;  // Least input wire not known to be in the
                             // subfield

  std::vector<Layer<Field>> l;  // layers

  uint8_t id[32];  // unique id for the circuit, created by the compiler

  bool operator==(const Circuit& y) const {
    return nv == y.nv && logv == y.logv && nc == y.nc && logc == y.logc &&
           nl == y.nl && l == y.l;
  }
  size_t nterms() const {
    size_t n = 0;
    for (const auto& layer : l) {
      n += layer.nterms();
    }
    return n;
  }
};

template <class Field>
struct LayerProof {
  using Elt = typename Field::Elt;
  // For efficiency, we distinguish polynomials needed to bind copy
  // variables (CPoly, degree 3) from polynomials needed to bind
  // wire variables (WPoly, degree 2).
  using CPoly = Poly<4, Field>;
  using WPoly = Poly<3, Field>;

  // Maximum 2^40 gates/wires/copies per layer.
  static constexpr size_t kMaxBindings = 40;

  CPoly cp[kMaxBindings];  // polys for the C variables

  // The binding order we use is "for (round) { for (hand) ... }", and
  // thus one can organize this array as [kMaxBindings][2] for better
  // memory locality.
  // However, the corresponding challenges are organized as [2][kMaxBindings]
  // to allow easier binding by hand, and so it makes sense to keep this
  // array in the same order as the challenges.
  WPoly hp[2][kMaxBindings];  // polys for each hand \in {right,left}

  // prover provides W[R,C] and W[L,C], which serve as claims
  // for the next layer
  Elt wc[2];
};

template <class Field>
struct LayerChallenge {
  using Elt = typename Field::Elt;
  static constexpr size_t kMaxBindings = LayerProof<Field>::kMaxBindings;

  // verifier: coefficient for the random linear combination
  // claim[0] + alpha * claim[1] of the two input claims.
  Elt alpha;
  Elt beta;                 // random coefficient for assert-zero
  Elt cb[kMaxBindings];     // bindings for the C variables
  Elt hb[2][kMaxBindings];  // bindings for each hand
};

template <class Field>
struct Challenge {
  using Elt = typename Field::Elt;
  static constexpr size_t kMaxBindings = LayerProof<Field>::kMaxBindings;

  // verifier picks Q for EQ[Q|c]
  Elt q[kMaxBindings];  // [logC]

  // verifier picks G for V[G,c]
  Elt g[kMaxBindings];  // [logV]
  std::vector<LayerChallenge<Field>> l;
  explicit Challenge(size_t nl) : l(nl) {}
};

// Full proof:
template <class Field>
struct Proof {
  typedef typename LayerProof<Field>::CPoly CPoly;
  typedef typename LayerProof<Field>::WPoly WPoly;

  using Elt = typename Field::Elt;
  static constexpr size_t kMaxBindings = LayerProof<Field>::kMaxBindings;

  // then engage in sumcheck one per layer
  std::vector<LayerProof<Field>> l;

  explicit Proof(size_t nl) : l(nl) {}
  size_t size() const {
    return l.size() * (kMaxBindings * 4 + kMaxBindings * 3 * 2 + 2);
  }
};

// Auxiliary information generated by the prover to be
// used by the ZK prover
template <class Field>
struct ProofAux {
  using Elt = typename Field::Elt;
  std::vector<Elt> bound_quad;
  explicit ProofAux(size_t nl) : bound_quad(nl) {}
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_SUMCHECK_CIRCUIT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_NODE_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_NODE_H_

#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <vector>

#include "sumcheck/quad.h"
#include "util/crc64.h"
#include "util/panic.h"

namespace proofs {

struct term {
  // size_t surrogate for storing things like depth and
  // pointers to nodes, since we only really need 32 bits.
  using size_t_for_storage = uint32_t;

  size_t_for_storage ki;  // index of the constant term into constants_[]
  size_t_for_storage op0, op1;

  term() = default;

  // canonicalized by op0 <= op1
  explicit term(size_t ki, size_t op0, size_t op1)
      : ki(ki),
        op0(std::min<size_t>(op0, op1)),
        op1(std::max<size_t>(op0, op1)) {
    // Terms with k=0 are not supposed to occur, since
    // we represent a zero node as an empty list of terms.
    // We represent Elt(0) as index ki=0 in the table of constants.
    proofs::check(ki != 0, "ki != 0");
  }

  // special hack for assert0
  struct assert0_type_hack {};  // so that we don't call this constructor
                                // accidentally
  explicit term(size_t op, assert0_type_hack& hack)
      : ki(/*kstore(f.zero())=*/0), op0(0), op1(op) {}

  bool ltndx(const term& y) const {
    if (op1 < y.op1) return true;
    if (op1 > y.op1) return false;
    return op0 < y.op0;
  }
  bool eqndx(const term& y) const { return (op1 == y.op1 && op0 == y.op0); }

  // term is a constant
  bool constant() const { return op0 == 0 && op1 == 0; }

  // linear term k * (1 * op1)
  bool linearp() const { return op0 == 0; }

  bool operator==(const term& y) const {
    return ki == y.ki && op0 == y.op0 && op1 == y.op1;
  }
};

template <class Field>
struct NodeInfoF {
  using quad_corner_t = typename Quad<Field>::quad_corner_t;
  using size_t_for_storage = term::size_t_for_storage;

  static const constexpr quad_corner_t kWireIdUndefined = quad_corner_t(-1);

  size_t_for_storage depth;
  quad_corner_t desired_wire_id_for_input;
  quad_corner_t desired_wire_id_for_output;
  size_t_for_storage max_needed_depth;
  bool is_needed;
  bool is_output;
  bool is_input;
  bool is_assert0;

  NodeInfoF()
      : depth(0),
        desired_wire_id_for_input(kWireIdUndefined),
        desired_wire_id_for_output(kWireIdUndefined),
        max_needed_depth(0),
        is_needed(false),
        is_output(false),
        is_input(false),
        is_assert0(false) {}

  // we use the desired wire id only at the appropriate depth,
  // and not e.g. for copy wires.
  quad_corner_t desired_wire_id(size_t depth0, size_t depth_ub) const {
    if (is_input && depth0 == 0) {
      return desired_wire_id_for_input;
    }
    if (is_output && depth0 + 1 == depth_ub) {
      return desired_wire_id_for_output;
    }
    return kWireIdUndefined;
  }
};

template <class Field>
struct NodeF {
  using nodeinfo = NodeInfoF<Field>;
  using quad_corner_t = typename Quad<Field>::quad_corner_t;

  std::vector<term> terms;
  nodeinfo info;

  NodeF() = delete;
  explicit NodeF(quad_corner_t id) : terms() {
    info.is_input = true;
    info.desired_wire_id_for_input = id;
  }

  explicit NodeF(size_t ki, size_t op0, size_t op1) : terms() {
    if (ki != 0) {
      terms.push_back(term(ki, op0, op1));
    }
  }

  explicit NodeF(const std::vector<term>& terms) : terms(terms) {}

  bool zero() const { return !info.is_input && terms.empty(); }
  bool constant() const { return terms.size() == 1 && terms[0].constant(); }
  bool linearp() const { return terms.size() == 1 && terms[0].linearp(); }

  bool operator==(const NodeF& y) const {
    if (info.is_input != y.info.is_input) return false;
    if (info.desired_wire_id_for_input != y.info.desired_wire_id_for_input)
      return false;
    if (info.is_output != y.info.is_output) return false;
    if (info.desired_wire_id_for_output != y.info.desired_wire_id_for_output)
      return false;
    if (info.is_input != y.info.is_input) return false;
    if (terms.size() != y.terms.size()) return false;
    size_t l = terms.size();
    for (size_t i = 0; i < l; ++i) {
      if (!(terms[i] == y.terms[i])) return false;
    }
    return true;
  }
  uint64_t hash() const {
    uint64_t crc = 0x1;
    crc = crc64::update(crc,
                        static_cast<uint64_t>(info.desired_wire_id_for_input));
    crc = crc64::update(crc,
                        static_cast<uint64_t>(info.desired_wire_id_for_output));
    crc = crc64::update(crc, info.is_input);
    crc = crc64::update(crc, info.is_output);
    size_t l = terms.size();
    crc = crc64::update(crc, l);
    for (size_t i = 0; i < l; ++i) {
      crc = crc64::update(crc, terms[i].ki);
      crc = crc64::update(crc, terms[i].op0);
      crc = crc64::update(crc, terms[i].op1);
    }
    return crc;
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_NODE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_REED_SOLOMON_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_REED_SOLOMON_H_

#include <stddef.h>

#include <memory>
#include <vector>

#include "algebra/utility.h"

namespace proofs {

/*
The ReedSolomon class interpolates a polynomial given as input in point-eval
form at a set of different points, thereby computing a form of RS encoding.
Specifically, the input polynomial of degree d=n-1 is given as evaluations
at 0, 1, 2, ..., n-1, and the output is the values at n, n+1, n+2, ..., n+m-1.
The algorithm uses the following relation:

  p(k) = (-1)^d (k-d)(k choose d) sum_{j=0}^{d} (1/k-j)(-1)^j (d choose j)p(j)

which can be efficiently computed using a convolution, whose implementation
is provided by a ConvolutionFactory for the field.

The const Field& objects that are passed have lifetimes that exceed the call
durations and can be safely passed by const reference.

*/
template <class Field, class ConvolutionFactory>
class ReedSolomon {
  using Elt = typename Field::Elt;
  using Convolver = typename ConvolutionFactory::Convolver;

 public:
  // n is the number of points provided
  // m is the total number of points output (including the initial n points)
  ReedSolomon(size_t n, size_t m, const Field& F,
              const ConvolutionFactory& factory)
      : f_(F),  // could grab this from the factory
        degree_bound_(n - 1),
        m_(m),
        leading_constant_(m - n + 1),
        binom_i_(n) {
    // inverses[i]: inverses[i] = 1/i from i = 1 to m-1 (inverses[0] = 0)
    std::vector<Elt> inverses(m_);
    AlgebraUtil<Field>::batch_inverse_arithmetic(m, &inverses[0], F);
    c_ = factory.make(n, m, &inverses[0]);
    leading_constant_[0] = F.one();
    binom_i_[0] = F.one();
    // Set leading_constant_[i] = (i+degree_bound_) choose degree_bound_
    // (from i=0 to i=m)
    for (size_t i = 1; i + degree_bound_ < m; ++i) {
      leading_constant_[i] =
          F.mulf(leading_constant_[i - 1],
                 F.mulf(F.of_scalar(degree_bound_ + i), inverses[i]));
    }
    // Finish computing the leading constants:
    // (-1)^degree_bound_ (k-degree_bound_) \binom{k}{degree_bound_}
    for (size_t k = degree_bound_; k < m; ++k) {
      F.mul(leading_constant_[k - degree_bound_],
            F.of_scalar(k - degree_bound_));
      if (degree_bound_ % 2 == 1) {
        F.neg(leading_constant_[k - degree_bound_]);
      }
    }

    for (size_t i = 1; i < n; ++i) {
      binom_i_[i] =
          F.mulf(binom_i_[i - 1], F.mulf(F.of_scalar(n - i), inverses[i]));
    }
    for (size_t i = 1; i < n; i += 2) {
      F.neg(binom_i_[i]);
    }
  }

  // Given the values of a polynomial of degree at most n at 0, 1, 2, ..., n-1,
  // this computes the values at n, n+1, n+2, ..., m-1.
  // (n points go in, m points come out)
  void interpolate(Elt y[/*m*/]) const {
    // shorthands
    const Field& F = f_;
    size_t n = degree_bound_ + 1;  // number of points input

    // Define x[i] = (-1)^i \binom{n}{i} p(i) for i=0 through i=n
    std::vector<Elt> x(n);
    for (size_t i = 0; i < n; i++) {
      x[i] = F.mulf(binom_i_[i], y[i]);
    }

    std::vector<Elt> T(m_);
    c_->convolution(&x[0], &T[0]);
    // Multiply the leading constants by the convolution
    for (size_t i = n; i < m_; ++i) {
      y[i] = F.mulf(leading_constant_[i - degree_bound_], T[i]);
    }
  }

 private:
  const Field& f_;

  // n is the number of points input, and degree_bound = n + 1.
  // degree_bound_ is useful since the LaTeX math is written in terms of it
  const size_t degree_bound_;  // degree bound, i.e., n - 1
  // total number of points output (points in + new points out)
  const size_t m_;

  std::unique_ptr<const Convolver> c_;

  // leading_constant_[i] = \binom{i+degree_bound_}{degree_bound_} *
  // (-1)^{degree_bound_} (i+degree_bound_ - degree_bound_) (from i=0 to i=m-n)
  // i.e., the leading constant \binom{k}{degree_bound_} *
  // (-1)^degree_bound_ (k - degree_bound_), shifted left by degree_bound_
  std::vector<Elt> leading_constant_;
  // (-1)^i (degree_bound_ choose i) from i=0 to i=degree_bound_
  std::vector<Elt> binom_i_;
};

template <class Field, class ConvolutionFactory>
class ReedSolomonFactory {
 public:
  ReedSolomonFactory(const ConvolutionFactory& factory, const Field& f)
      : factory_(factory), f_(f) {}

  std::unique_ptr<ReedSolomon<Field, ConvolutionFactory>> make(size_t n,
                                                               size_t m) const {
    return std::make_unique<ReedSolomon<Field, ConvolutionFactory>>(n, m, f_,
                                                                    factory_);
  }

 private:
  const ConvolutionFactory& factory_;
  const Field& f_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_REED_SOLOMON_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_LIMB_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_LIMB_H_

#include <array>
#include <cstddef>
#include <cstdint>

#include "util/serialization.h"

namespace proofs {

// Base class for representing bignum or bigpoly as arrays of
// machine-dependent "limbs".  The serialization is in this
// class; arithmetic is in subclasses.

template <size_t W64>
class Limb {
 public:
  using T = Limb<W64>;

#if __WORDSIZE == 64
  using limb_t = uint64_t;
#else
  using limb_t = uint32_t;
#endif

  // sizes in bytes, bits, limbs, uint64_t
  static constexpr size_t kBytes = 8 * W64;
  static constexpr size_t kBits = 64 * W64;
  static constexpr size_t kLimbs = kBytes / sizeof(limb_t);
  static constexpr size_t kU64 = W64;
  static constexpr size_t kBitsPerLimb = 8 * sizeof(limb_t);

  // no rounding allowed
  static_assert(kLimbs * sizeof(limb_t) == kBytes);

  limb_t limb_[kLimbs];

  Limb() = default;  // uninitialized
  explicit Limb(uint64_t x) : limb_{} { assign(limb_, 1, &x); }

  explicit Limb(const std::array<uint64_t, kU64>& a) : limb_{} {
    assign(limb_, kU64, &a[0]);
  }

  std::array<uint64_t, kU64> u64() const {
    std::array<uint64_t, kU64> a;
    unassign(limb_, kU64, &a[0]);
    return a;
  }

  void to_bytes(uint8_t a[/* kBytes */]) const {
    for (size_t i = 0; i < kLimbs; ++i) {
      a = to_bytes(&limb_[i], a);
    }
  }

  bool operator==(const T& other) const {
    for (size_t i = 0; i < kLimbs; ++i) {
      if (limb_[i] != other.limb_[i]) {
        return false;
      }
    }
    return true;
  }
  bool operator!=(const T& other) const { return !(operator==(other)); }

  // Shift right by z.  Return the bits that fall off
  // the edge.
  limb_t shiftr(size_t z) {
    limb_t c = 0;
    for (size_t i = kLimbs; i-- > 0;) {
      limb_t d = limb_[i];
      limb_[i] = c | (d >> z);
      c = d << (kBitsPerLimb - z);
    }
    return c;
  }

  // Returns the pos-th bit in the representation of this nat.
  limb_t bit(size_t pos) const {
    size_t ind = pos / kBitsPerLimb;
    if (ind < kLimbs) {
      size_t off = pos % kBitsPerLimb;
      return (limb_[ind] >> off) & 0x1u;
    }
    return 0;
  }

 protected:
  static void assign(uint64_t d[], size_t ns, const uint64_t s[/*ns*/]) {
    for (size_t i = 0; i < ns; ++i) {
      d[i] = s[i];
    }
  }

  static void assign(uint32_t d[], size_t ns, const uint64_t s[/*ns*/]) {
    for (size_t i = 0; i < ns; ++i) {
      d[2 * i] = static_cast<uint32_t>(s[i]);
      d[2 * i + 1] = static_cast<uint32_t>(s[i] >> 32);
    }
  }

  static void unassign(const uint64_t d[], size_t ns, uint64_t s[/*ns*/]) {
    for (size_t i = 0; i < ns; ++i) {
      s[i] = d[i];
    }
  }

  static void unassign(const uint32_t d[], size_t ns, uint64_t s[/*ns*/]) {
    for (size_t i = 0; i < ns; ++i) {
      s[i] = d[2 * i] | (static_cast<uint64_t>(d[2 * i + 1]) << 32);
    }
  }

  static const uint8_t* of_bytes(uint64_t* r, const uint8_t* a) {
    *r = u64_of_le(a);
    return a + 8;
  }
  static const uint8_t* of_bytes(uint32_t* r, const uint8_t* a) {
    *r = u32_of_le(a);
    return a + 4;
  }

  static uint8_t* to_bytes(const uint64_t* r, uint8_t* a) {
    u64_to_le(a, *r);
    return a + 8;
  }
  static uint8_t* to_bytes(const uint32_t* r, uint8_t* a) {
    u32_to_le(a, *r);
    return a + 4;
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_LIMB_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_MERKLE_MERKLE_TREE_H_
#define PRIVACY_PROOFS_ZK_LIB_MERKLE_MERKLE_TREE_H_

#include <stddef.h>
#include <stdint.h>

#include <cstring>
#include <vector>

#include "util/crypto.h"
#include "util/panic.h"

namespace proofs {

// This package computes and verifies Merkle Tree inclusion claims.
// The standard Merkle tree algorithm has been implemented.

// A digest of a Merkle tree.
struct Digest {
  static constexpr size_t kLength = kSHA256DigestSize;
  uint8_t data[kLength];

  bool operator==(const Digest& y) const {
    return memcmp(data, y.data, kLength) == 0;
  }

  static Digest hash2(const Digest& L, const Digest& R) {
    SHA256 sha;
    sha.Update(L.data, kLength);
    sha.Update(R.data, kLength);
    Digest output;
    sha.DigestData(output.data);
    return output;
  }
};

// Return the length of the proof for N leaves.
// Mimic the code in generate_proof() without actually
// computing the proof
inline size_t merkle_tree_len(size_t n) {
  size_t r = 1;
  size_t pos = (n - 1);  // maximum possible value
  for (pos += n; pos > 1; pos >>= 1) {
    ++r;
  }
  return r;
}

// compute the set of all nodes on the path from the
// root to any leaf in POS.
inline std::vector<bool> compressed_merkle_proof_tree(size_t n,
                                                      const size_t pos[/*np*/],
                                                      size_t np) {
  check(np > 0, "A Merkle proof with 0 leaves is not defined.");
  std::vector<bool> tree(2 * n, false);

  // leaves are in TREE
  for (size_t ip = 0; ip < np; ++ip) {
    check(pos[ip] < n, "Invalid position for leaf in Merkle tree");
    tree[pos[ip] + n] = true;
  }

  // If a child of an inner node is in TREE, then the parent is in TREE.
  for (size_t i = n; i-- > 1;) {
    tree[i] = (tree[2 * i] || tree[2 * i + 1]);
  }

  // Assert that the root is in TREE.
  check(tree[1], "tree[1]");

  return tree;
}

class MerkleTree {
 public:
  explicit MerkleTree(size_t n) : n_(n), layers_(2 * n) {}

  void set_leaf(size_t pos, const Digest& leaf) {
    check(pos < n_, "Invalid position for leaf in Merkle tree");
    layers_[pos + n_] = leaf;
  }

  Digest build_tree() {
    for (size_t i = n_; i-- > 1;) {
      layers_[i] = Digest::hash2(layers_[2 * i], layers_[2 * i + 1]);
    }
    return layers_[1];
  }

  // The generate_proof method writes a Merkle tree proof for the leaf
  // at position pos into the proof array and returns the size of the proof
  // in number of Digests.
  size_t generate_proof(Digest proof[/*logn+1*/], size_t pos) const {
    Digest* begin = proof;
    *proof++ = layers_[pos + n_];
    for (pos += n_; pos > 1; pos >>= 1) {
      *proof++ = layers_[pos ^ 1];
    }
    return (proof - begin);
  }

  // Compressed Merkle proofs over a set POS[NP] of leaves.
  //
  // We first compute the set TREE of all nodes that are on the path
  // from the root to any leaf in POS.  Then, for each inner node in
  // TREE, we include in the proof the child that is not in TREE, if
  // any.
  size_t generate_compressed_proof(std::vector<Digest>& proof,
                                   const size_t pos[/*np*/], size_t np) {
    std::vector<bool> tree = compressed_merkle_proof_tree(n_, pos, np);

    // For each TREE node, include in the proof the
    // child that is not TREE, if any.
    size_t sz = 0;
    for (size_t i = n_; i-- > 1;) {
      if (tree[i]) {
        size_t child = 2 * i;
        if (tree[child]) {
          // try the other child
          child = 2 * i + 1;
        }
        if (!tree[child]) {
          proof.push_back(layers_[child]);
          ++sz;
        }
      }
    }
    return sz;
  }

  size_t n_;
  // layers_[n, 2 * n) stores the leaves (nodes at layer 0).
  // layers_[n/2, n) stores nodes at layer 1.
  // layers_[n/4, n/2) stores nodes at layer 2, etc.
  // The root is at layers_[1] where layers_[0] is not used.
  std::vector<Digest> layers_;
};

class MerkleTreeVerifier {
 public:
  explicit MerkleTreeVerifier(size_t n, const Digest& root)
      : n_(n), root_(root) {}

  bool verify_proof(const Digest* proof, size_t pos) const {
    Digest t = *proof++;
    for (pos += n_; pos > 1; pos >>= 1) {
      t = (pos & 1) ? Digest::hash2(*proof++, t) : Digest::hash2(t, *proof++);
    }
    return t == root_;
  }

  bool verify_compressed_proof(const Digest* proof, size_t proof_len,
                               const Digest leaves[/*np*/],
                               const size_t pos[/*np*/], size_t np) const {
    // Reconstructed layers_, where only the DEFINED subset is
    // defined.
    std::vector<Digest> layers(2 * n_, Digest{});
    std::vector<bool> defined(2 * n_, false);

    /*scope for TREE */ {
      std::vector<bool> tree = compressed_merkle_proof_tree(n_, pos, np);

      // read the proof
      size_t sz = 0;
      for (size_t i = n_; i-- > 1;) {
        if (tree[i]) {
          size_t child = 2 * i;
          if (tree[child]) {
            // try the other child
            child = 2 * i + 1;
          }
          if (!tree[child]) {
            if (sz >= proof_len) {
              return false;
            }
            layers[child] = proof[sz++];
            defined[child] = true;
          }
        }
      }
    }

    // set LAYERS at all leaves in POS
    for (size_t ip = 0; ip < np; ++ip) {
      size_t l = pos[ip] + n_;
      layers[l] = leaves[ip];
      defined[l] = true;
    }

    // Recompute as many inner nodes as we can
    for (size_t i = n_; i-- > 1;) {
      if (defined[2 * i] && defined[2 * i + 1]) {
        layers[i] = Digest::hash2(layers[2 * i], layers[2 * i + 1]);
        defined[i] = true;
      }
    }

    return (defined[1] && (root_ == layers[1]));
  }

 private:
  size_t n_;
  Digest root_;
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_MERKLE_MERKLE_TREE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_SYSDEP_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_SYSDEP_H_

#include <stddef.h>

#include <cstdint>

#include "util/panic.h"  // IWYU pragma: keep

#if defined(__x86_64__) || defined(__i386__)
// system-dependent basic arithmetic functions: add with carry
// and 64x64->128 bit multiplication
#include <x86intrin.h>  // IWYU pragma: keep
#endif

namespace proofs {

#if defined(__x86_64__)
static inline uint64_t adc(uint64_t* a, uint64_t b, uint64_t c) {
  // unsigned long long (not uint64_t) is *required* by the
  // _addcarry_u64() prototype.  uint64_t is unsigned long on
  // linux, and pointers to the two types are incompatible even
  // though the conversion is a no-op.
  unsigned long long out;
  c = _addcarry_u64(c, *a, b, &out);
  *a = out;
  return c;
}
static inline uint32_t adc(uint32_t* a, uint32_t b, uint32_t c) {
  return _addcarry_u32(c, *a, b, a);
}
static inline uint64_t sbb(uint64_t* a, uint64_t b, uint64_t c) {
  unsigned long long out;
  c = _subborrow_u64(c, *a, b, &out);
  *a = out;
  return c;
}
static inline uint32_t sbb(uint32_t* a, uint32_t b, uint32_t c) {
  return _subborrow_u32(c, *a, b, a);
}
static inline void mulq(uint64_t* l, uint64_t* h, uint64_t a, uint64_t b) {
  asm("mulx %2, %0, %1" : "=r"(*l), "=r"(*h) : "r"(b), "d"(a));
}
#elif defined(__i386__)
static inline uint32_t adc(uint32_t* a, uint32_t b, uint32_t c) {
  return _addcarry_u32(c, *a, b, a);
}
static inline uint32_t sbb(uint32_t* a, uint32_t b, uint32_t c) {
  return _subborrow_u32(c, *a, b, a);
}

// these two functions are supposed to be defined but are
// never called
static inline unsigned long long adc(unsigned long long* a,
                                     unsigned long long b,
                                     unsigned long long c) {
  check(false, "adcll() not defined");
  return 0;
}
static inline unsigned long long sbb(unsigned long long* a,
                                     unsigned long long b,
                                     unsigned long long c) {
  check(false, "sbbll() not defined");
  return 0;
}

#define SYSDEP_MULQ64_NOT_DEFINED
#elif defined(__clang__)
// The clang intrinsics use the builtin-types int, long, etc.
// Thus we define adc() and sbb() in terms of those types.
static inline unsigned long long adc(unsigned long long* a,
                                     unsigned long long b,
                                     unsigned long long c) {
  *a = __builtin_addcll(*a, b, c, &c);
  return c;
}
static inline unsigned long adc(unsigned long* a, unsigned long b,
                                unsigned long c) {
  *a = __builtin_addcl(*a, b, c, &c);
  return c;
}
static inline unsigned int adc(unsigned int* a, unsigned int b,
                               unsigned int c) {
  *a = __builtin_addc(*a, b, c, &c);
  return c;
}

static inline unsigned long long sbb(unsigned long long* a,
                                     unsigned long long b,
                                     unsigned long long c) {
  *a = __builtin_subcll(*a, b, c, &c);
  return c;
}
static inline unsigned long sbb(unsigned long* a, unsigned long b,
                                unsigned long c) {
  *a = __builtin_subcl(*a, b, c, &c);
  return c;
}
static inline unsigned int sbb(unsigned int* a, unsigned int b,
                               unsigned int c) {
  *a = __builtin_subc(*a, b, c, &c);
  return c;
}

#if defined(__SIZEOF_INT128__)
// It seems that __SIZEOF_INT128__ is defined if __uint128_t is.
static inline void mulq(uint64_t* l, uint64_t* h, uint64_t a, uint64_t b) {
  __uint128_t p = (__uint128_t)b * (__uint128_t)a;
  *l = p;
  *h = p >> 64;
}
#else  // defined(__SIZEOF_INT128__)
#define SYSDEP_MULQ64_NOT_DEFINED
#endif  // defined(__SIZEOF_INT128__)
#endif

static inline void mulq(uint32_t* l, uint32_t* h, uint32_t a, uint32_t b) {
  uint64_t p = (uint64_t)b * (uint64_t)a;
  *l = p;
  *h = p >> 32;
}

// Identity function whose only purpose is to confuse the compiler.
// We have no coherent theory of when and why this is useful, but
// here are a couple of cases where this hack makes a difference:
//
// * Passing the cmov() values through identity_limb() seems
//   to favor the generation of a conditional move instruction
//   as opposed to a conditional branch.
// * Clang and gcc match a+b+carry to generate the adcq instruction,
//   but a+0+carry becomes a+carry and the match fails.  So
//   we pretend that the zero is not a zero.
// * A similar issue arises in subtract with carry.
//
// This function is obviously a hack.  Works for me today but YMMV.
//
template <class limb_t>
static inline limb_t identity_limb(limb_t v) {
  asm("" : "+r"(v)::);
  return v;
}

template <class limb_t>
static inline limb_t zero_limb() {
  return identity_limb<limb_t>(0);
}

// a += b
template <class limb_t>
static inline void accum(size_t Wa, limb_t a[/*Wa*/], size_t Wb,
                         const limb_t b[/*Wb*/]) {
  limb_t c = 0;
  for (size_t i = 0; i < Wb; ++i) {
    c = adc(&a[i], b[i], c);
  }
  for (size_t i = Wb; i < Wa; ++i) {
    c = adc(&a[i], 0, c);
  }
}

// a -= b
template <class limb_t>
static inline void negaccum(size_t Wa, limb_t a[/*Wa*/], size_t Wb,
                            const limb_t b[/*Wb*/]) {
  limb_t c = 0;
  for (size_t i = 0; i < Wb; ++i) {
    c = sbb(&a[i], b[i], c);
  }
  for (size_t i = Wb; i < Wa; ++i) {
    c = sbb(&a[i], 0, c);
  }
}

// h::a += b
template <class limb_t>
static inline limb_t add_limb(size_t W, limb_t a[/*W*/],
                              const limb_t b[/*W*/]) {
  limb_t c = 0;
  for (size_t i = 0; i < W; ++i) {
    c = adc(&a[i], b[i], c);
  }
  limb_t h = zero_limb<limb_t>();
  c = adc(&h, 0, c);
  return h;
}

// h::a += b * 2^(bits per limb)
template <class limb_t>
static inline limb_t addh(size_t W, limb_t a[/*W*/], const limb_t b[/*W*/]) {
  limb_t c = 0;
  for (size_t i = 1; i < W; ++i) {
    c = adc(&a[i], b[i - 1], c);
  }
  limb_t h = zero_limb<limb_t>();
  c = adc(&h, b[W - 1], c);
  return h;
}

// h::a -= b
template <class limb_t>
static inline limb_t sub_limb(size_t W, limb_t a[/*W*/],
                              const limb_t b[/*W*/]) {
  limb_t c = 0;
  for (size_t i = 0; i < W; ++i) {
    c = sbb(&a[i], b[i], c);
  }
  limb_t h = zero_limb<limb_t>();
  c = sbb(&h, 0, c);
  return h;
}

// h:l = a*b
template <class limb_t>
static inline void mulhl(size_t W, limb_t l[/*W*/], limb_t h[/*W*/], limb_t a,
                         const limb_t b[/*W*/]) {
  for (size_t i = 0; i < W; ++i) {
    mulq(&l[i], &h[i], a, b[i]);
  }
}

// a = b
template <class limb_t>
static inline void mov(size_t W, limb_t a[/*W*/], const limb_t b[/*W*/]) {
  for (size_t i = 0; i < W; ++i) {
    a[i] = b[i];
  }
}

// It seems that using assembly code is the only way to
// force gcc and clang to use conditional moves.
#if defined(__x86_64__)
static inline void cmovnz(size_t W, uint64_t a[/*W*/], uint64_t nz,
                          const uint64_t b[/*W*/]) {
  if (W == 1) {
    asm("testq %[nz], %[nz]\n\t"
        "cmovneq %[b0], %[a0]\n\t"
        : [a0] "+r"(a[0])
        : [nz] "r"(nz), [b0] "r"(b[0]));
  } else if (W == 2) {
    asm("testq %[nz], %[nz]\n\t"
        "cmovneq %[b0], %[a0]\n\t"
        "cmovneq %[b1], %[a1]\n\t"
        : [a0] "+r"(a[0]), [a1] "+r"(a[1])
        : [nz] "r"(nz), [b0] "r"(b[0]), [b1] "r"(b[1]));
  } else if (W == 3) {
    asm("testq %[nz], %[nz]\n\t"
        "cmovneq %[b0], %[a0]\n\t"
        "cmovneq %[b1], %[a1]\n\t"
        "cmovneq %[b2], %[a2]\n\t"
        : [a0] "+r"(a[0]), [a1] "+r"(a[1]), [a2] "+r"(a[2])
        : [nz] "r"(nz), [b0] "r"(b[0]), [b1] "r"(b[1]), [b2] "r"(b[2]));
  } else if (W == 4) {
    asm("testq %[nz], %[nz]\n\t"
        "cmovneq %[b0], %[a0]\n\t"
        "cmovneq %[b1], %[a1]\n\t"
        "cmovneq %[b2], %[a2]\n\t"
        "cmovneq %[b3], %[a3]\n\t"
        : [a0] "+r"(a[0]), [a1] "+r"(a[1]), [a2] "+r"(a[2]), [a3] "+r"(a[3])
        : [nz] "r"(nz), [b0] "r"(b[0]), [b1] "r"(b[1]), [b2] "r"(b[2]),
          [b3] "r"(b[3]));
  } else {
    for (size_t i = 0; i < W; ++i) {
      a[i] = (nz != 0) ? b[i] : a[i];
    }
  }
}

static inline void cmovne(size_t W, uint64_t a[/*W*/], uint64_t x, uint64_t y,
                          const uint64_t b[/*W*/]) {
  if (W == 1) {
    asm("cmpq %[x], %[y]\n\t"
        "cmovneq %[b0], %[a0]\n\t"
        : [a0] "+r"(a[0])
        : [x] "r"(x), [y] "r"(y), [b0] "r"(b[0])
        : "cc");
  } else if (W == 2) {
    asm("cmpq %[x], %[y]\n\t"
        "cmovneq %[b0], %[a0]\n\t"
        "cmovneq %[b1], %[a1]\n\t"
        : [a0] "+r"(a[0]), [a1] "+r"(a[1])
        : [x] "r"(x), [y] "r"(y), [b0] "r"(b[0]), [b1] "r"(b[1])
        : "cc");
  } else if (W == 3) {
    asm("cmpq %[x], %[y]\n\t"
        "cmovneq %[b0], %[a0]\n\t"
        "cmovneq %[b1], %[a1]\n\t"
        "cmovneq %[b2], %[a2]\n\t"
        : [a0] "+r"(a[0]), [a1] "+r"(a[1]), [a2] "+r"(a[2])
        : [x] "r"(x), [y] "r"(y), [b0] "r"(b[0]), [b1] "r"(b[1]), [b2] "r"(b[2])
        : "cc");
  } else if (W == 4) {
    asm("cmpq %[x], %[y]\n\t"
        "cmovneq %[b0], %[a0]\n\t"
        "cmovneq %[b1], %[a1]\n\t"
        "cmovneq %[b2], %[a2]\n\t"
        "cmovneq %[b3], %[a3]\n\t"
        : [a0] "+r"(a[0]), [a1] "+r"(a[1]), [a2] "+r"(a[2]), [a3] "+r"(a[3])
        : [x] "r"(x), [y] "r"(y), [b0] "r"(b[0]), [b1] "r"(b[1]),
          [b2] "r"(b[2]), [b3] "r"(b[3])
        : "cc");
  } else {
    for (size_t i = 0; i < W; ++i) {
      a[i] = (x != y) ? b[i] : a[i];
    }
  }
}

static inline uint64_t addcmovc(uint64_t a, uint64_t b, uint64_t c) {
  asm("add %[b], %[a]\n\t"
      "cmovaeq %[c], %[a]\n\t"
      : [a] "+r"(a)
      : [b] "r"(b), [c] "r"(c)
      : "cc");
  return a;
}

static inline uint64_t sub_sysdep(uint64_t a, uint64_t y, uint64_t m) {
  uint64_t z = 0;
  asm("subq %[y], %[a]\n\t"
      "cmovbq %[m], %[z]\n\t"
      : [a] "+r"(a), [z] "+r"(z)
      : [y] "r"(y), [m] "r"(m)
      : "cc");
  return a + z;
}

#elif defined(__aarch64__)

static inline void cmovne(size_t W, uint64_t a[/*W*/], uint64_t x, uint64_t y,
                          const uint64_t b[/*W*/]) {
  if (W == 1) {
    asm("cmp %[x], %[y]\n\t"                //
        "csel %[a0], %[a0], %[b0], eq\n\t"  //
        : [a0] "+r"(a[0])                   //
        : [x] "r"(x), [y] "ri"(y),          //
          [b0] "r"(b[0])                    //
        : "cc");
  } else if (W == 2) {
    asm("cmp %[x], %[y]\n\t"                //
        "csel %[a0], %[a0], %[b0], eq\n\t"  //
        "csel %[a1], %[a1], %[b1], eq\n\t"  //
        : [a0] "+r"(a[0]),                  //
          [a1] "+r"(a[1])                   //
        : [x] "r"(x), [y] "ri"(y),          //
          [b0] "r"(b[0]),                   //
          [b1] "r"(b[1])                    //
        : "cc");
  } else if (W == 3) {
    asm("cmp %[x], %[y]\n\t"                //
        "csel %[a0], %[a0], %[b0], eq\n\t"  //
        "csel %[a1], %[a1], %[b1], eq\n\t"  //
        "csel %[a2], %[a2], %[b2], eq\n\t"  //
        : [a0] "+r"(a[0]),                  //
          [a1] "+r"(a[1]),                  //
          [a2] "+r"(a[2])                   //
        : [x] "r"(x), [y] "ri"(y),          //
          [b0] "r"(b[0]),                   //
          [b1] "r"(b[1]),                   //
          [b2] "r"(b[2])                    //
        : "cc");
  } else if (W == 4) {
    asm("cmp %[x], %[y]\n\t"                //
        "csel %[a0], %[a0], %[b0], eq\n\t"  //
        "csel %[a1], %[a1], %[b1], eq\n\t"  //
        "csel %[a2], %[a2], %[b2], eq\n\t"  //
        "csel %[a3], %[a3], %[b3], eq\n\t"  //
        : [a0] "+r"(a[0]),                  //
          [a1] "+r"(a[1]),                  //
          [a2] "+r"(a[2]),                  //
          [a3] "+r"(a[3])                   //
        : [x] "r"(x), [y] "ri"(y),          //
          [b0] "r"(b[0]),                   //
          [b1] "r"(b[1]),                   //
          [b2] "r"(b[2]),                   //
          [b3] "r"(b[3])                    //
        : "cc");
  } else {
    for (size_t i = 0; i < W; ++i) {
      a[i] = (x != y) ? b[i] : a[i];
    }
  }
}

// a = (nz != 0) ? b : a
static inline void cmovnz(size_t W, uint64_t a[/*W*/], uint64_t nz,
                          const uint64_t b[/*W*/]) {
  constexpr uint64_t z = 0;
  cmovne(W, a, nz, z, b);
}

static inline uint64_t addcmovc(uint64_t a, uint64_t b, uint64_t c) {
  asm("adds %[a], %[a], %[b]\n\t"
      "csel %[a], %[a], %[c], hs\n\t"
      : [a] "+r"(a)
      : [b] "r"(b), [c] "r"(c)
      : "cc");
  return a;
}

static inline uint64_t sub_sysdep(uint64_t a, uint64_t y, uint64_t m) {
  asm("subs %[a], %[a], %[y]\n\t"
      "csel %[m], %[m], xzr, lo"
      : [a] "+r"(a), [m] "+r"(m)
      : [y] "r"(y)
      : "cc");
  return a + m;
}

#else  // generic portable code

// a = (x != y) ? b : a
template <class limb_t>
static inline void cmovne(size_t W, limb_t a[/*W*/], limb_t x, limb_t y,
                          const limb_t b[/*W*/]) {
  for (size_t i = 0; i < W; ++i) {
    a[i] = (x != y) ? b[i] : a[i];
  }
}

// a = (nz != 0) ? b : a
template <class limb_t>
static inline void cmovnz(size_t W, limb_t a[/*W*/], limb_t nz,
                          const limb_t b[/*W*/]) {
  constexpr limb_t z = 0;
  cmovne(W, a, nz, z, b);
}

template <class limb_t>
static inline limb_t addcmovc(limb_t a, limb_t b, limb_t c) {
  limb_t t = a + b;
  return (a > t) ? t : c;
}

template <class limb_t>
static inline limb_t sub_sysdep(limb_t a, limb_t y, limb_t m) {
  limb_t t0 = a - y;
  return (y > a) ? (t0 + m) : t0;
}

#endif

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_SYSDEP_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_SCAN_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_SCAN_H_

#include <stddef.h>

#include <vector>

namespace proofs {
template <class Logic>
class Scan {
 public:
  using EltW = typename Logic::EltW;
  using BitW = typename Logic::BitW;

  explicit Scan(const Logic& l) : l_(l) {}

  /* Segmented prefix add, equivalent to this code:

       s = 0;
       for (size_t i = 0; i < n; ++i) {
         if (S[i]) {
           s = A[i];
         } else {
           s += ds[i];
         }
         B[i] = s;
       }
  */
  void add(size_t n, EltW B[/*n*/], const BitW S[/*n*/], const EltW A[/*n*/],
           const EltW ds[/*n*/]) {
    const Logic& L = l_;  // shorthand
    std::vector<BitW> S1(n);
    for (size_t i = 0; i < n; ++i) {
      S1[i] = S[i];
      B[i] = L.mux(&S[i], &A[i], ds[i]);
    }
    scan_add(0, n, S1.data(), B);
  }

  // unsegmented variant of add(), assume S[i] = false
  void add(size_t n, EltW B[/*n*/], const EltW ds[/*n*/]) {
    for (size_t i = 0; i < n; ++i) {
      B[i] = ds[i];
    }
    scan_add(0, n, B);
  }

 private:
  const Logic& l_;

  void scan_add(size_t i0, size_t i1, BitW S[/*n*/], EltW B[/*n*/]) {
    if (i1 - i0 > 1) {
      const Logic& L = l_;  // shorthand
      size_t im = i0 + (i1 - i0) / 2;
      scan_add(i0, im, S, B);
      scan_add(im, i1, S, B);

      size_t j = im - 1;
      for (size_t i = im; i < i1; ++i) {
        // special case of B[i] = S[i] ? B[i] : B[i] + B[j]
        // coded as B[i] = B[i] + (~S[i] * B[j])
        auto ns = L.lnot(S[i]);
        auto ns_bj = L.lmul(&ns, B[j]);
        B[i] = L.add(&B[i], ns_bj);
        S[i] = L.lor(&S[i], S[j]);
      }
    }
  }

  // unsegmented
  void scan_add(size_t i0, size_t i1, EltW B[/*n*/]) {
    if (i1 - i0 > 1) {
      const Logic& L = l_;  // shorthand
      size_t im = i0 + (i1 - i0) / 2;
      scan_add(i0, im, B);
      scan_add(im, i1, B);

      size_t j = im - 1;
      for (size_t i = im; i < i1; ++i) {
        B[i] = L.add(&B[j], B[i]);
      }
    }
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_SCAN_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_CIRCUIT_ID_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_CIRCUIT_ID_H_

#include <stddef.h>

#include <cstdint>

#include "circuits/compiler/circuit_id.h"
#include "sumcheck/circuit.h"
#include "util/crypto.h"

namespace proofs {

// This method produces a unique name for a circuit. It does not match
// the serialization method for the circuit.
template <class Field>
void circuit_id(uint8_t id[/*32*/], const Circuit<Field>& c, const Field& F) {
  const uint64_t CHAR2 = 0x2;
  const uint64_t ODD = 0x1;
  SHA256 sha;
  uint8_t tmp[Field::kBytes];
  if (F.kCharacteristicTwo) {
    // Characteristic two fields are uniquely determined by their length
    // in our codebase.
    sha.Update8(CHAR2);  // Indicates binary field.
    sha.Update8(F.kBits);
  } else {
    // Prime fields are determined by -1.
    sha.Update8(ODD);  // Indicates odd prime field.
    F.to_bytes_field(tmp, F.mone());
    sha.Update(tmp, sizeof(tmp));
  }
  sha.Update8(c.nv);
  sha.Update8(c.logv);
  sha.Update8(c.nc);
  sha.Update8(c.logc);
  sha.Update8(c.nl);
  sha.Update8(c.ninputs);
  sha.Update8(c.npub_in);
  sha.Update8(c.subfield_boundary);
  for (const auto& layer : c.l) {
    sha.Update8(layer.nw);
    sha.Update8(layer.logw);
    sha.Update8(layer.quad->n_);
    for (size_t i = 0; i < layer.quad->n_; ++i) {
      sha.Update8(static_cast<uint64_t>(layer.quad->c_[i].g));
      sha.Update8(static_cast<uint64_t>(layer.quad->c_[i].h[0]));
      sha.Update8(static_cast<uint64_t>(layer.quad->c_[i].h[1]));
      F.to_bytes_field(tmp, layer.quad->c_[i].v);
      sha.Update(tmp, sizeof(tmp));
    }
  }
  sha.DigestData(id);
}

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_CIRCUIT_ID_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FFT_INTERPOLATION_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FFT_INTERPOLATION_H_

#include <stddef.h>
#include <stdint.h>

#include <vector>

#include "algebra/blas.h"
#include "algebra/twiddle.h"
#include "util/panic.h"

namespace proofs {
template <class Field>
class FFTInterpolation {
  using Elt = typename Field::Elt;

  // Know a0, a1 want b0, b1
  // Note winv = w^{-1}
  static void a0a1(const Elt* A, Elt* B, size_t s, const Elt& winv,
                   const Field& F) {
    Elt x0 = A[0];
    Elt x1 = F.mulf(A[s], winv);
    B[0] = F.addf(x0, x1);
    B[s] = F.subf(x0, x1);
  }

  static void a0a1(const Elt* A, Elt* B, size_t s, const Field& F) {
    Elt x0 = A[0];
    Elt x1 = A[s];
    B[0] = F.addf(x0, x1);
    B[s] = F.subf(x0, x1);
  }

  // know b0, b1 want a0, a1
  static void b0b1(Elt* A, const Elt* B, size_t s, const Elt& w,
                   const Field& F) {
    Elt x0 = F.mulf(F.half(), F.addf(B[0], B[s]));
    Elt x1 = F.mulf(F.half(), F.subf(B[0], B[s]));
    A[0] = x0;
    A[s] = F.mulf(x1, w);
  }

  static void b0b1_unscaled(Elt* A, const Elt* B, size_t s, const Elt& w,
                            const Field& F) {
    Elt x0 = F.addf(B[0], B[s]);
    Elt x1 = F.subf(B[0], B[s]);
    A[0] = x0;
    A[s] = F.mulf(x1, w);
  }
  static void b0b1_unscaled(Elt* A, const Elt* B, size_t s, const Field& F) {
    Elt x0 = F.addf(B[0], B[s]);
    Elt x1 = F.subf(B[0], B[s]);
    A[0] = x0;
    A[s] = x1;
  }

  // know: a0 and b0, want a1 and b1
  // x0 = a0
  // x1 = a1 * w^{-1}
  // b0 = x0 + x1
  // b1 = x0 - x1
  static void a0b0(Elt* A, Elt* B, size_t s, const Elt& w, const Field& F) {
    Elt x0 = A[0];
    Elt x1 = F.subf(B[0], x0);
    A[s] = F.mulf(x1, w);
    B[s] = F.subf(x0, x1);
  }

  // know: a0 and b1, want a1 and b0
  // x0 = a0
  // x1 = a1 * w^{-1}
  // b0 = x0 + x1
  // b1 = x0 - x1
  static void a0b1(Elt* A, Elt* B, size_t s, const Elt& w, const Field& F) {
    Elt x0 = A[0];
    Elt x1 = F.subf(x0, B[s]);
    A[s] = F.mulf(x1, w);
    B[0] = F.addf(x0, x1);
  }

  // B -> A
  static void fftb(Elt A[/*n*/], const Elt B[/*n*/], size_t n,
                   const Twiddle<Field>& roots, const Field& F) {
    for (size_t j = 0; j < n; ++j) {
      A[j] = B[j];
    }

    Elt scale = F.one();

    for (size_t m = n; m > 2;) {
      m /= 2;
      size_t ws = roots.order_ / (2 * m);
      for (size_t k = 0; k < n; k += 2 * m) {
        b0b1_unscaled(&A[k], &A[k], m, F);  // j == 0
        for (size_t j = 1; j < m; ++j) {
          b0b1_unscaled(&A[k + j], &A[k + j], m, roots.w_[j * ws], F);
        }
      }
      F.mul(scale, F.half());
    }

    if (n >= 2) {
      for (size_t k = 0; k < n; k += 2) {
        b0b1_unscaled(&A[k], &A[k], 1, F);
      }
      F.mul(scale, F.half());
    }

    Blas<Field>::scale(n, A, 1, scale, F);
  }

  // A -> B
  static void fftf(const Elt A[/*n*/], Elt B[/*n*/], size_t n,
                   const Twiddle<Field>& rootsinv, const Field& F) {
    for (size_t j = 0; j < n; ++j) {
      B[j] = A[j];
    }

    // m = 1
    if (n >= 2) {
      for (size_t k = 0; k < n; k += 2) {
        a0a1(&B[k], &B[k], 1, F);
      }
    }

    // m > 1
    for (size_t m = 2; m < n; m = 2 * m) {
      size_t ws = rootsinv.order_ / (2 * m);
      for (size_t k = 0; k < n; k += 2 * m) {
        a0a1(&B[k], &B[k], m, F);  // j = 0
        for (size_t j = 1; j < m; ++j) {
          a0a1(&B[k + j], &B[k + j], m, rootsinv.w_[j * ws], F);
        }
      }
    }
  }

  static bool in_range(size_t j, size_t b0, size_t n, size_t k) {
    size_t b1 = b0 + (n - k);
    return (b0 <= j && j < b1) || (b0 <= j + n && j + n < b1);
  }

  // This is a generalization of the truncated FFT algorithm described
  // in Joris van der Hoeven, "The Truncated Fourier Transform and
  // Applications".  See also the followup paper "Notes on the
  // Truncated Fourier Transform", also by Joris van der Hoeven.

  // Define arbitrarily an "evaluation" domain A and a "coefficient"
  // domain B.  The "forward" FFT computes the cofficients B given
  // evaluations A, and the "backward" FFT computes the evaluations A
  // given the coefficients B.  By convention, the evaluations A are in
  // bit-reversed order, and we put the 1/N normalization on
  // the backward side.

  // Given inputs
  //
  //    A[j] for 0 <= j < k
  //    B[j % n] for b0 <= j < b0 + (n - k)
  //
  // this function fills the rest of A[] and B[], so that at the
  // end B = fftf(A) and A = fftb(B).
  static void bidir(size_t n, Elt A[/*n*/], Elt B[/*n*/], size_t k, size_t b0,
                    const Twiddle<Field>& roots, const Twiddle<Field>& rootsinv,
                    Elt workspace[/*2*n*/], const Field& F) {
    check(k <= n, "k <= n");
    check(b0 < n, "b0 < n");

    if (k == 0) {
      fftb(A, B, n, roots, F);
    } else if (k == n) {
      fftf(A, B, n, rootsinv, F);
    } else if (n > 1) {
      size_t ws = roots.order_ / n;
      size_t n2 = n / 2;

      // allocate T from workspace
      Elt* T = workspace;
      workspace += n;

      if (k >= n2) {
        // first half A -> T
        fftf(A, &T[0], n2, rootsinv, F);

        // diagonal butterflies T <-> B
        for (size_t j = 0; j < n2; ++j) {
          if (in_range(j, b0, n, k)) {
            if (in_range(j + n2, b0, n, k)) {
              // can't happen because the range is < n2
              check(false, "can't happen");
            } else {
              a0b0(&T[j], &B[j], n2, roots.w_[j * ws], F);
            }
          } else {
            if (in_range(j + n2, b0, n, k)) {
              a0b1(&T[j], &B[j], n2, roots.w_[j * ws], F);
            } else {
              // done below
            }
          }
        }

        // second half A <-> T
        size_t bb0 = (b0 >= n2) ? (b0 - n2) : b0;
        bidir(n2, &A[n2], &T[n2], k - n2, bb0, roots, rootsinv, workspace, F);

        // forward butterflies T -> B
        for (size_t j = 0; j < n2; ++j) {
          if (in_range(j, b0, n, k)) {
            if (in_range(j + n2, b0, n, k)) {
              // can't happen because the range is < n2
              check(false, "can't happen");
            } else {
              // done above
            }
          } else {
            if (in_range(j + n2, b0, n, k)) {
              // done above
            } else {
              a0a1(&T[j], &B[j], n2, rootsinv.w_[j * ws], F);
            }
          }
        }
      } else {
        // backward butterflies B -> T
        for (size_t j = 0; j < n2; ++j) {
          if (in_range(j, b0, n, k)) {
            if (in_range(j + n2, b0, n, k)) {
              b0b1(&T[j], &B[j], n2, roots.w_[j * ws], F);
            } else {
              // done below
            }
          } else {
            // done below
          }
        }

        // first half A <-> T
        size_t bb0 = (b0 >= n2) ? (b0 - n2) : b0;
        bidir(n2, &A[0], &T[0], k, bb0, roots, rootsinv, workspace, F);

        // diagonal butterflies T <-> B
        for (size_t j = 0; j < n2; ++j) {
          if (in_range(j, b0, n, k)) {
            if (in_range(j + n2, b0, n, k)) {
              // done above
            } else {
              a0b0(&T[j], &B[j], n2, roots.w_[j * ws], F);
            }
          } else {
            if (in_range(j + n2, b0, n, k)) {
              a0b1(&T[j], &B[j], n2, roots.w_[j * ws], F);
            } else {
              // can't happen.  Range is >= n2 so
              // either j or j+n2 is in range
              check(false, "can't happen");
            }
          }
        }

        // second half T -> A
        fftb(&A[n2], &T[n2], n2, roots, F);
      }
    }
  }

 public:
  static void interpolate(size_t n, Elt A[/*n*/], Elt B[/*n*/], size_t k,
                          size_t b0, const Elt& omega_m, uint64_t m,
                          const Field& F) {
    if (n > 1) {
      Elt omega_n = Twiddle<Field>::reroot(omega_m, m, n, F);
      Twiddle<Field> roots(n, omega_n, F);
      Twiddle<Field> rootsinv(n, F.invertf(omega_n), F);
      std::vector<Elt> workspace(2 * n);
      bidir(n, A, B, k, b0, roots, rootsinv, &workspace[0], F);
    } else if (n == 1) {
      // Twiddle(n) fails because of vector of size 0.
      // Compute the answer directly.
      if (k == 0) {
        A[0] = B[0];
      } else {
        B[0] = A[0];
      }
    }
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FFT_INTERPOLATION_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_ENCODER_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_ENCODER_H_

#include <stddef.h>
#include <stdint.h>

#include <array>

#include "circuits/logic/bit_plucker_constants.h"

namespace proofs {
template <class Field, size_t LOGN>
class BitPluckerEncoder {
  const Field& f_;

  using Elt = typename Field::Elt;
  static constexpr size_t kN = size_t(1) << LOGN;
  static constexpr size_t kNv32Elts = (32u + LOGN - 1u) / LOGN;
  static constexpr size_t kNv128Elts = (128u + LOGN - 1u) / LOGN;
  static constexpr size_t kNv256Elts = (256u + LOGN - 1u) / LOGN;

 public:
  using packed_v32 = std::array<Elt, kNv32Elts>;
  using packed_v128 = std::array<Elt, kNv128Elts>;
  using packed_v256 = std::array<Elt, kNv256Elts>;

  explicit BitPluckerEncoder(const Field& F) : f_(F) {}

  Elt encode(size_t i) const { return bit_plucker_point<Field, kN>()(i, f_); }

  // Special case packer for uint32_t used in sha256.
  packed_v32 mkpacked_v32(uint32_t j) {
    packed_v32 r;
    for (size_t i = 0; i < r.size(); ++i) {
      r[i] = encode(j & (kN - 1));
      j >>= LOGN;
    }
    return r;
  }

  template <typename T>
  T pack(uint8_t bits[/* n bits */], size_t n) {
    T r;
    for (size_t i = 0; i < r.size(); ++i) {
      size_t v = 0;
      for (size_t j = 0; j < LOGN; ++j) {
        if (i * LOGN + j < n) {
          v += (bits[i * LOGN + j] & 0x1) << j;
        }
      }
      r[i] = encode(v);
    }
    return r;
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_ENCODER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_POLY_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_POLY_H_

#include <cstddef>

namespace proofs {
// Fixed-size N-tuples of field elements, interpreted as polynomial coefficients
// and/or values and/or newton expansion.
template <size_t N, class Field>
class Poly {
 public:
  static const size_t kN = N;
  using Elt = typename Field::Elt;
  using T = Poly;

  // the N-tuple itself
  Elt t_[N];

  Elt& operator[](size_t i) { return t_[i]; }
  const Elt& operator[](size_t i) const { return t_[i]; }

  T& add(const T& y, const Field& F) {
    for (size_t i = 0; i < N; ++i) {
      F.add(t_[i], y[i]);
    }
    return *this;
  }
  T& sub(const T& y, const Field& F) {
    for (size_t i = 0; i < N; ++i) {
      F.sub(t_[i], y[i]);
    }
    return *this;
  }
  T& mul(const T& y, const Field& F) {
    for (size_t i = 0; i < N; ++i) {
      F.mul(t_[i], y[i]);
    }
    return *this;
  }
  T& mul_scalar(const Elt& y, const Field& F) {
    for (size_t i = 0; i < N; ++i) {
      F.mul(t_[i], y);
    }
    return *this;
  }

  static T extend(const Poly<2, Field>& f, const Field& F) {
    T g;
    g[0] = f[0];
    g[1] = f[1];
    Elt df = F.subf(f[1], f[0]);

    if (Field::kCharacteristicTwo) {
      // Assume poly_evaluation_point[0] = 0, poly_evaluation_point[1] = 1,
      // and the rest are arbitrary.
      for (size_t i = 2; i < N; ++i) {
        g[i] = F.addf(g[0], F.mulf(F.poly_evaluation_point(i), df));
      }
    } else {
      // Assume that poly_evaluation_point[] form an arithmetic
      // progression.
      for (size_t i = 2; i < N; ++i) {
        g[i] = F.addf(g[i - 1], df);
      }
    }

    return g;
  }

  // convert Lagrange basis -> Newton forward differences for the
  // special case of evaluation points 0, 1, 2, ..., N-1.
  // See interpolation.h for the general case of interpolation.
  void newton_of_lagrange(const Field& F) {
    for (size_t i = 1; i < N; i++) {
      for (size_t k = N; k-- > i;) {
        F.sub(t_[k], t_[k - 1]);
        F.mul(t_[k], F.newton_denominator(k, i));
      }
    }
  }

  // Evaluate f(x) for a polynomial in the Newton forward-difference
  // basis.
  Elt eval_newton(const Elt& x, const Field& F) const {
    // Newton interpolation formula
    Elt e = t_[N - 1];
    for (size_t i = N - 1; i-- > 0;) {
      F.mul(e, F.subf(x, F.poly_evaluation_point(i)));
      F.add(e, t_[i]);
    }

    return e;
  }

  Elt eval_lagrange(const Elt& x, const Field& F) const {
    T tmp(*this);  // do not clobber *this
    tmp.newton_of_lagrange(F);
    return tmp.eval_newton(x, F);
  }

  // Evaluate f(r) given a polynomial in the standard basis
  // f(x)=t_[i]*x^i.
  Elt eval_monomial(const Elt& x, const Field& F) const {
    // Horner's algorithm
    Elt e = t_[N - 1];
    for (size_t i = N - 1; i-- > 0;) {
      F.mul(e, x);
      F.add(e, t_[i]);
    }
    return e;
  }

  static T powers_of(const Elt& e, const Field& F) {
    T r;
    r[0] = F.one();
    for (size_t i = 1; i < N; ++i) {
      r[i] = F.mulf(r[i - 1], e);
    }
    return r;
  }

  // Interpolation via explicit dot product.
  //
  // The combination P.newton_of_lagrange().eval_newton(..., R, ...)
  // evaluates P at R given the Lagrange basis [P(0), P(1), ..., P(N-1)].
  //
  // On the contrary, this class computes a V(R) such that P(R) =
  // dot(V(R), [P(0), P(1), ..., P(N-1)]) and the caller computes the
  // inner product, either explicitly or via an inner-product
  // argument.  The construction is pure linear algebra: express the
  // Lagrange basis P = [P(0), P(1), ..., P(N-1)]^T as I * P where I
  // is the identity matrix, and interpolate the rows of I
  // via newton_of_lagrange().eval_newton().  Since newton_of_lagrange()
  // is O(N^2) and eval_newton() is O(N), pre-compute the eval_newton()
  // of all rows.
  class dot_interpolation {
    // identity_[k] contains the Newton basis of the polynomial P(x) such
    // that P(k) = 1 and P(i) = 0 for i != k and 0 <= i < N.
    T identity_[N];

   public:
    explicit dot_interpolation(const Field& F) {
      for (size_t k = 0; k < N; ++k) {
        for (size_t i = 0; i < N; ++i) {
          identity_[k][i] = (i == k) ? F.one() : F.zero();
        }
        identity_[k].newton_of_lagrange(F);
      }
    }

    // return V such that P(r) = V^T [P(0), P(1), ..., P(N-1)]
    T coef(const Elt& x, const Field& F) const {
      T c;
      for (size_t k = 0; k < N; ++k) {
        c[k] = identity_[k].eval_newton(x, F);
      }
      return c;
    }
  };
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_POLY_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_REFERENCE_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_REFERENCE_H_

#include <cstddef>
#include <cstdint>
#include <vector>

#include "arrays/dense.h"
#include "random/random.h"
#include "util/panic.h"

namespace proofs {

template <class GF>
class MACReference {
  using gf2k = typename GF::Elt;

 public:
  void sample(gf2k ap[], size_t n, RandomEngine* rng) {
    check(n > 0, "n must be positive");
    std::vector<uint8_t> buf(n * GF::kBytes);
    rng->bytes(buf.data(), n * GF::kBytes);
    for (size_t i = 0; i < n; ++i) {
      ap[i] = gf_.of_bytes_field(&buf[i * GF::kBytes]).value();
    }
  }

  // Computes the mac of a 32-byte message.
  void compute(gf2k mac[/*2*/], const gf2k& av, const gf2k ap[/*2*/],
               uint8_t msg[/*32*/]) const {
    uint8_t tmp[GF::kBytes] = {0};
    for (size_t i = 0; i < 2; ++i) {
      memcpy(tmp, &msg[i * GF::kBytes], GF::kBytes);
      gf2k m = gf_.of_bytes_field(tmp).value();
      mac[i] = gf_.mulf(gf_.addf(av, ap[i]), m);
    }
  }

  void to_bytes(gf2k mac[/*2*/], uint8_t buf[/* 32 */]) {
    gf_.to_bytes(mac[0], buf);
    gf_.to_bytes(mac[1], buf + GF::kBytes);
  }

 private:
  GF gf_;
};

template <typename GF, typename Field>
void fill_gf2k(const typename GF::Elt& m, DenseFiller<Field>& df,
               const Field& f) {
  for (size_t i = 0; i < GF::kBits; ++i) {
    df.push_back(m[i] ? f.one() : f.zero());
  }
}

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MAC_MAC_REFERENCE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_POLYNOMIAL_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_POLYNOMIAL_H_

#include <stddef.h>

#include <array>

#include "algebra/poly.h"
#include "util/ceildiv.h"

namespace proofs {
template <class Logic>
class Polynomial {
 public:
  using Field = typename Logic::Field;
  using BitW = typename Logic::BitW;
  using EltW = typename Logic::EltW;
  const Logic& l_;

  explicit Polynomial(const Logic& l) : l_(l) {}

  void powers_of_x(size_t n, EltW xi[/*n*/], const EltW& x) const {
    const Logic& L = l_;  // shorthand

    if (n > 0) {
      xi[0] = L.konst(1);
      if (n > 1) {
        xi[1] = x;
        // invariant: xi[i] = x**i for i < k.
        // Extend inductively to k = n.
        for (size_t k = 2; k < n; ++k) {
          xi[k] = L.mul(&xi[k - k / 2], xi[k / 2]);
        }
      }
    }
  }

  // Evaluation via dot product with coefficients
  template <size_t N>
  EltW eval(const Poly<N, Field>& coef, const EltW& x) const {
    const Logic& L = l_;  // shorthand

    std::array<EltW, N> xi;
    powers_of_x(N, xi.data(), x);

    // dot product with coefficients
    EltW r = L.konst(0);
    for (size_t i = 0; i < N; ++i) {
      auto cxi = L.mul(coef[i], xi[i]);
      r = L.add(&r, cxi);
    }
    return r;
  }

  // Evaluation via parallel Horner's rule
  template <size_t N>
  EltW eval_horner(const Poly<N, Field>& coef, EltW x) const {
    const Logic& L = l_;  // shorthand

    std::array<EltW, N> c;
    for (size_t i = 0; i < N; ++i) {
      c[i] = L.konst(coef[i]);
    }

    for (size_t n = N; n > 1; n = ceildiv<size_t>(n, 2)) {
      for (size_t i = 0; 2 * i < n; ++i) {
        c[i] = c[2 * i];
        if (2 * i + 1 < n) {
          auto cxi = L.mul(&x, c[2 * i + 1]);
          c[i] = L.add(&c[i], cxi);
        }
      }
      x = L.mul(&x, x);
    }
    return c[0];
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_POLYNOMIAL_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_RFFT_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_RFFT_H_

#include <stddef.h>
#include <stdint.h>

#include "algebra/permutations.h"
#include "algebra/twiddle.h"
#include "util/panic.h"

namespace proofs {

// Real FFT and its inverse.
//
// The FFT F[j] of a real input R[k] is complex and
// conjugate-symmetric: F[j] = conj(F[n - j]).
//
// Following the FFTW conventions, to avoid doubling the
// storage, we store F[j] as a "half-complex" array HC[j] of elements
// in the base field.
//
//   HC[j] = (2j <= n) ? real(F[j]) : imag(F[n - j])
//
// Thus we have two kinds of transforms: R2HC (real to
// half-complex) and HC2R (half-complex to real).
//
// Again following the FFTW conventions, we say that
// the R2HC transform is "forward" (minus sign in the exponent)
// and the HC2R sign is "backward" (plus sign in the exponent).
// See fft.h for a definition of forward and backward.

template <class FieldExt>
class RFFT {
  using Field = typename FieldExt::BaseField;
  using RElt = typename Field::Elt;
  using CElt = typename FieldExt::Elt;

  // The machinery in this file only works if the root is
  // on the unit circle, because we multiply by the conjugate
  // instead of by the inverse.
  static void validate_root(const CElt& omega, const FieldExt& C) {
    check(C.mulf(omega, C.conjf(omega)) == C.one(),
          "root of unity not on the unit circle");
  }

  static void r2hcI(RElt* A, size_t s, const Field& R) {
    RElt t = A[s];
    A[s] = A[0];
    R.add(A[0], t);
    R.sub(A[s], t);
  }
  static void r2hcII(RElt* A, size_t s, const CElt& tw, const Field& R) {
    R.mul(A[s], R.negf(tw.im));
  }

  static void hc2hcf(RElt* Ar, RElt* Ai, size_t s, const CElt& tw,
                     const Field& R) {
    RElt xr, xi;
    cmulj(&xr, &xi, Ar[s], Ai[s], tw.re, tw.im, R);
    RElt ar0 = Ar[0];
    RElt ai0 = Ai[0];
    Ar[0] = R.addf(ar0, xr);
    Ai[0] = R.subf(ar0, xr);
    Ar[s] = R.subf(xi, ai0);
    Ai[s] = R.addf(xi, ai0);
  }

  static void hc2rI(RElt* A, size_t s, const Field& R) {
    RElt t = A[s];
    A[s] = A[0];
    R.add(A[0], t);
    R.sub(A[s], t);
  }

  static void hc2rIII(RElt* A, size_t s, const CElt& tw, const Field& R) {
    R.add(A[0], A[0]);
    R.add(A[s], A[s]);
    R.mul(A[s], R.negf(tw.im));
  }

  static void hc2hcb(RElt* Ar, RElt* Ai, size_t s, const CElt& tw,
                     const Field& R) {
    RElt ar0 = Ar[0];
    RElt ai0 = Ai[0];
    RElt ar1 = Ar[s];
    RElt ai1 = Ai[s];
    Ar[0] = R.addf(ar0, ai0);
    Ai[0] = R.subf(ai1, ar1);
    RElt xr = R.subf(ar0, ai0);
    RElt xi = R.addf(ai1, ar1);
    cmul(&Ar[s], &Ai[s], xr, xi, tw.re, tw.im, R);
  }

 public:
  // Forward real to half-complex in-place transform.
  // N (the length of A) must be a power of 2
  static void r2hc(RElt A[/*n*/], size_t n, const CElt& omega,
                   uint64_t omega_order, const FieldExt& C) {
    validate_root(omega, C);

    if (n <= 1) {
      return;
    }

    const Field& R = C.base_field();
    CElt omega_n = Twiddle<FieldExt>::reroot(omega, omega_order, n, C);
    Twiddle<FieldExt> roots(n, omega_n, C);

    Permutations<RElt>::bitrev(A, n);

    // m=1 iteration
    for (size_t k = 0; k < n; k += 2) {
      r2hcI(&A[k], 1, R);
    }

    // m>1 iterations
    for (size_t m = 2; m < n; m = 2 * m) {
      size_t ws = n / (2 * m);
      for (size_t k = 0; k < n; k += 2 * m) {
        size_t j;
        r2hcI(&A[k], m, R);  // j==0

        for (j = 1; j + j < m; ++j) {
          hc2hcf(&A[k + j], &A[k + m - j], m, roots.w_[j * ws], R);
        }

        r2hcII(&A[k + j], m, roots.w_[j * ws], R);  // j==m/2
      }
    }
  }

  // Backward half-complex to real in-place transform.
  static void hc2r(RElt A[/*n*/], size_t n, const CElt& omega,
                   uint64_t omega_order, const FieldExt& C) {
    validate_root(omega, C);

    if (n <= 1) {
      return;
    }

    const Field& R = C.base_field();
    CElt omega_n = Twiddle<FieldExt>::reroot(omega, omega_order, n, C);
    Twiddle<FieldExt> roots(n, omega_n, C);

    // m>1 iterations
    for (size_t m = n; (m /= 2) >= 2;) {
      size_t ws = n / (2 * m);
      for (size_t k = 0; k < n; k += 2 * m) {
        size_t j;
        hc2rI(&A[k], m, R);  // j==0

        for (j = 1; j + j < m; ++j) {
          hc2hcb(&A[k + j], &A[k + m - j], m, roots.w_[j * ws], R);
        }

        hc2rIII(&A[k + j], m, roots.w_[j * ws], R);  // j==m/2
      }
    }

    // m=1 iteration
    for (size_t k = 0; k < n; k += 2) {
      hc2rI(&A[k], 1, R);
    }

    Permutations<RElt>::bitrev(A, n);
  }

  // X = A * B
  static void cmul(RElt* xr, RElt* xi, const RElt& ar, const RElt& ai,
                   const RElt& br, const RElt& bi, const Field& R) {
    // Karatsuba 3 mul + 5 add
    auto p0 = R.mulf(ar, br);
    auto p1 = R.mulf(ai, bi);
    auto a01 = R.addf(ar, ai);
    auto b01 = R.addf(br, bi);
    *xr = R.subf(p0, p1);
    R.mul(a01, b01);
    R.sub(a01, p0);
    R.sub(a01, p1);
    *xi = a01;
  }

  // X = A * conj(B)
  static void cmulj(RElt* xr, RElt* xi, const RElt& ar, const RElt& ai,
                    const RElt& br, const RElt& bi, const Field& R) {
    // Karatsuba 3 mul + 5 add
    auto p0 = R.mulf(ar, br);
    auto p1 = R.mulf(ai, bi);
    auto a01 = R.addf(ar, ai);
    auto b01 = R.subf(br, bi);
    *xr = R.addf(p0, p1);
    R.mul(a01, b01);
    R.sub(a01, p0);
    R.add(a01, p1);
    *xi = a01;
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_RFFT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_VERIFIER_H_
#define PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_VERIFIER_H_

#include <stddef.h>

#include <array>
#include <vector>

#include "algebra/blas.h"
#include "ligero/ligero_param.h"
#include "ligero/ligero_transcript.h"
#include "merkle/merkle_commitment.h"
#include "random/transcript.h"
#include "util/crypto.h"

namespace proofs {
template <class Field, class InterpolatorFactory>
class LigeroVerifier {
  using Elt = typename Field::Elt;

 public:
  static void receive_commitment(const LigeroCommitment<Field>& commitment,
                                 Transcript& ts) {
    // P -> V
    LigeroTranscript<Field>::write_commitment(commitment, ts);
  }

  static bool verify(const char** why, const LigeroParam<Field>& p,
                     const LigeroCommitment<Field>& commitment,
                     const LigeroProof<Field>& proof, Transcript& ts, size_t nl,
                     size_t nllterm,
                     const LigeroLinearConstraint<Field> llterm[/*nllterm*/],
                     const LigeroHash& hash_of_llterm, const Elt b[/*nl*/],
                     const LigeroQuadraticConstraint lqc[/*nq*/],
                     const InterpolatorFactory& interpolator, const Field& F) {
    if (why == nullptr) {
      return false;
    }

    std::vector<Elt> u_ldt(p.nwqrow);
    std::vector<Elt> alphal(nl);
    std::vector<std::array<Elt, 3>> alphaq(p.nq);
    std::vector<Elt> u_quad(p.nqtriples);
    std::vector<size_t> idx(p.nreq);

    // Replay the protocol first in order to compute all the
    // challenges.  In particular, we need IDX before we can do
    // anything useful.

    // P -> V
    ts.write(hash_of_llterm.bytes, hash_of_llterm.kLength);

    // V -> P
    LigeroTranscript<Field>::gen_uldt(&u_ldt[0], p, ts, F);

    // V -> P
    LigeroTranscript<Field>::gen_alphal(nl, &alphal[0], ts, F);
    LigeroTranscript<Field>::gen_alphaq(&alphaq[0], p, ts, F);

    // V -> P
    LigeroTranscript<Field>::gen_uquad(&u_quad[0], p, ts, F);

    // P -> V
    ts.write(&proof.y_ldt[0], 1, p.block, F);
    ts.write(&proof.y_dot[0], 1, p.dblock, F);
    ts.write(&proof.y_quad_0[0], 1, p.r, F);
    ts.write(&proof.y_quad_2[0], 1, p.dblock - p.block, F);

    // V -> P
    LigeroTranscript<Field>::gen_idx(&idx[0], p, ts, F);

    if (!merkle_check(p, commitment, proof, &idx[0], F)) {
      *why = "merkle_check failed";
      return false;
    }

    if (!low_degree_check(p, proof, &idx[0], &u_ldt[0], interpolator, F)) {
      *why = "low_degree_check failed";
      return false;
    }

    {
      // linear check
      std::vector<Elt> A(p.nwqrow * p.w);

      LigeroCommon<Field>::inner_product_vector(&A[0], p, nl, nllterm, llterm,
                                                &alphal[0], lqc, &alphaq[0], F);

      if (!dot_check(p, proof, &idx[0], &A[0], interpolator, F)) {
        *why = "dot_check failed";
        return false;
      }

      // check the putative value of the inner product
      Elt want_dot = Blas<Field>::dot(nl, b, 1, &alphal[0], 1, F);
      Elt proof_dot = Blas<Field>::dot1(p.w, &proof.y_dot[p.r], 1, F);
      if (want_dot != proof_dot) {
        *why = "wrong dot product";
        return false;
      }
    }

    if (!quadratic_check(p, proof, &idx[0], &u_quad[0], interpolator, F)) {
      *why = "quadratic_check failed";
      return false;
    }

    *why = "ok";
    return true;
  }

 private:
  static void interpolate_req_columns(Elt yp[/*nreq*/],
                                      const LigeroParam<Field>& p, size_t ylen,
                                      const Elt y[/*ylen*/],
                                      const size_t idx[/*nreq*/],
                                      const InterpolatorFactory& interpolator,
                                      const Field& F) {
    const auto interpy = interpolator.make(ylen, p.block_enc);
    std::vector<Elt> yext(p.block_enc);
    Blas<Field>::copy(ylen, &yext[0], 1, y, 1);
    interpy->interpolate(&yext[0]);
    Blas<Field>::gather(p.nreq, &yp[0], &yext[p.dblock], idx);
  }

  static bool merkle_check(const LigeroParam<Field>& p,
                           const LigeroCommitment<Field>& commitment,
                           const LigeroProof<Field>& proof,
                           const size_t idx[/*nreq*/], const Field& F) {
    auto updhash = [&](size_t r, SHA256& sha) {
      LigeroCommon<Field>::column_hash(p.nrow, &proof.req_at(0, r), p.nreq, sha,
                                       F);
    };

    return MerkleCommitmentVerifier::verify(p.block_enc - p.dblock,
                                            commitment.root, proof.merkle, idx,
                                            p.nreq, updhash);
  }

  static bool low_degree_check(const LigeroParam<Field>& p,
                               const LigeroProof<Field>& proof,
                               const size_t idx[/*nreq*/],
                               const Elt u_ldt[/*nrow*/],
                               const InterpolatorFactory& interpolator,
                               const Field& F) {
    std::vector<Elt> yc(p.nreq);

    // the ILDT blinding row with coefficient 1
    Blas<Field>::copy(p.nreq, &yc[0], 1, &proof.req_at(p.ildt, 0), 1);

    // all remaining rows with coefficient u_ldt[]
    for (size_t i = 0; i < p.nwqrow; ++i) {
      Blas<Field>::axpy(p.nreq, &yc[0], 1, u_ldt[i], &proof.req_at(i + p.iw, 0),
                        1, F);
    }

    std::vector<Elt> yp(p.nreq);
    interpolate_req_columns(&yp[0], p, p.block, &proof.y_ldt[0], idx,
                            interpolator, F);

    if (!Blas<Field>::equal(p.nreq, &yp[0], 1, &yc[0], 1, F)) {
      return false;
    }

    return true;
  }

  static bool dot_check(const LigeroParam<Field>& p,
                        const LigeroProof<Field>& proof,
                        const size_t idx[/*nreq*/], const Elt A[/*nwqrow, w*/],
                        const InterpolatorFactory& interpolator,
                        const Field& F) {
    std::vector<Elt> yc(p.nreq);

    // the IDOT blinding row with coefficient 1
    Blas<Field>::copy(p.nreq, &yc[0], 1, &proof.req_at(p.idot, 0), 1);

    {
      const auto interpA = interpolator.make(p.block, p.block_enc);

      std::vector<Elt> Aext(p.block_enc);
      std::vector<Elt> Areq(p.nreq);

      for (size_t i = 0; i < p.nwqrow; ++i) {
        LigeroCommon<Field>::layout_Aext(&Aext[0], p, i, &A[0], F);
        interpA->interpolate(&Aext[0]);
        Blas<Field>::gather(p.nreq, &Areq[0], &Aext[p.dblock], idx);

        // Accumulate z += A[j] \otimes W[j].
        Blas<Field>::vaxpy(p.nreq, &yc[0], 1, &Areq[0], 1,
                           &proof.req_at(i + p.iw, 0), 1, F);
      }
    }

    std::vector<Elt> yp(p.nreq);
    interpolate_req_columns(&yp[0], p, p.dblock, &proof.y_dot[0], idx,
                            interpolator, F);

    if (!Blas<Field>::equal(p.nreq, &yp[0], 1, &yc[0], 1, F)) {
      return false;
    }
    return true;
  }

  static bool quadratic_check(const LigeroParam<Field>& p,
                              const LigeroProof<Field>& proof,
                              const size_t idx[/*nreq*/],
                              const Elt u_quad[/*nqtriples*/],
                              const InterpolatorFactory& interpolator,
                              const Field& F) {
    std::vector<Elt> yc(p.nreq);

    // the IQUAD blinding row with coefficient 1
    Blas<Field>::copy(p.nreq, &yc[0], 1, &proof.req_at(p.iquad, 0), 1);

    {
      std::vector<Elt> tmp(p.nreq);
      size_t iqx = p.iq;
      size_t iqy = iqx + p.nqtriples;
      size_t iqz = iqy + p.nqtriples;

      // all quadratic triples with coefficient u_ldt[]
      for (size_t i = 0; i < p.nqtriples; ++i) {
        // yc += u_quad[i] * (z[i] - x[i] * y[i])

        // tmp = z[i]
        Blas<Field>::copy(p.nreq, &tmp[0], 1, &proof.req_at(iqz + i, 0), 1);

        // tmp -= x[i] \otimes y[i]
        Blas<Field>::vymax(p.nreq, &tmp[0], 1, &proof.req_at(iqx + i, 0), 1,
                           &proof.req_at(iqy + i, 0), 1, F);

        // yc += u_quad[i] * tmp
        Blas<Field>::axpy(p.nreq, &yc[0], 1, u_quad[i], &tmp[0], 1, F);
      }
    }

    // reconstruct y_quad from the two parts in the proof
    std::vector<Elt> yquad(p.dblock);
    Blas<Field>::copy(p.r, &yquad[0], 1, &proof.y_quad_0[0], 1);
    Blas<Field>::clear(p.w, &yquad[p.r], 1, F);
    Blas<Field>::copy(p.dblock - p.block, &yquad[p.block], 1,
                      &proof.y_quad_2[0], 1);

    // interpolate y_quad at the opened columns
    std::vector<Elt> yp(p.nreq);
    interpolate_req_columns(&yp[0], p, p.dblock, &yquad[0], idx, interpolator,
                            F);

    if (!Blas<Field>::equal(p.nreq, &yp[0], 1, &yc[0], 1, F)) {
      return false;
    }
    return true;
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_VERIFIER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_CIRCUIT_DUMP_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_CIRCUIT_DUMP_H_

#include <stddef.h>

#include "circuits/compiler/compiler.h"
#include "util/log.h"

// Debug printing routines for circuit tests.
namespace proofs {

template <class Field>
inline void dump_info(const char* name, size_t size,
                      const QuadCircuit<Field>& Q) {
  log(INFO, "Compiled circuit: %s[%zu]", name, size);
  dump_q(Q);
}

template <class Field>
inline void dump_info(const char* name, size_t sz0, size_t sz1,
                      const QuadCircuit<Field>& Q) {
  log(INFO, "Compiled circuit: %s[%zu][%zu]", name, sz0, sz1);
  dump_q(Q);
}

template <class Field>
inline void dump_info(const char* name, size_t sz0, size_t sz1, size_t sz2,
                      const QuadCircuit<Field>& Q) {
  log(INFO, "Compiled circuit: %s[%zu][%zu][%zu]", name, sz0, sz1, sz2);
  dump_q(Q);
}

template <class Field>
inline void dump_info(const char* name, const QuadCircuit<Field>& Q) {
  log(INFO, "Compiled circuit: %s", name);
  dump_q(Q);
}

template <class Field>
inline void dump_q(const QuadCircuit<Field>& Q) {
  log(INFO,
      " depth: %zu wires: %zu in: %zu out:%zu use:%zu ovh:%zu t:%zu cse:%zu "
      "notn:%zu",
      Q.depth_, Q.nwires_, Q.ninput_, Q.noutput_,
      Q.nwires_ - Q.nwires_overhead_, Q.nwires_overhead_, Q.nquad_terms_,
      Q.nwires_cse_eliminated_, Q.nwires_not_needed_);
}

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_COMPILER_CIRCUIT_DUMP_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_EVALUATION_BACKEND_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_EVALUATION_BACKEND_H_

#include "util/panic.h"

namespace proofs {
// backend that evaluates values directly
template <class Field>
class EvaluationBackend {
  using Elt = typename Field::Elt;

 public:
  explicit EvaluationBackend(const Field& F,
                             bool panic_on_assertion_failure = true)
      : f_(F),
        panic_on_assertion_failure_(panic_on_assertion_failure),
        assertion_failed_(false) {}

  ~EvaluationBackend() {
    // Crash if assertion_failed_, which indicates that a test
    // has forgotten to read the value
    check(!assertion_failed_, "assertion_failed_ true in ~EvaluationBackend()");
  }

  // Reading ASSERTION_FAILED_ returns the current ASSERTION_FAILED_
  // state and resets the state.
  bool assertion_failed() const {
    bool b = assertion_failed_;
    assertion_failed_ = false;
    return b;
  }

  struct V {
    Elt e;
    V() = default;
    explicit V(const Elt& x) : e(x) {}
    Elt elt() const { return e; }

    bool operator==(const V& y) const { return e == y.e; }
    bool operator!=(const V& y) const { return e != y.e; }
  };

  V assert0(const V& a) const {
    if (a.e == f_.zero()) {
      return a;
    } else {
      if (panic_on_assertion_failure_) {
        check(false, "a != F.zero()");
      }
      assertion_failed_ = true;
    }
    return a;
  }

  V add(const V& a, const V& b) const { return V{f_.addf(a.e, b.e)}; }
  V sub(const V& a, const V& b) const { return V{f_.subf(a.e, b.e)}; }
  V mul(const V& a, const V& b) const { return V{f_.mulf(a.e, b.e)}; }
  V mul(const Elt& a, const V& b) const { return V{f_.mulf(a, b.e)}; }
  V mul(const Elt& a, const V& b, const V& c) const {
    return mul(a, mul(b, c));
  }
  V konst(const Elt& a) const { return V{a}; }

  V ax(const Elt& a, const V& x) const { return V{f_.mulf(a, x.e)}; }
  V axy(const Elt& a, const V& x, const V& y) const {
    return V{f_.mulf(a, f_.mulf(x.e, y.e))};
  }
  V axpy(const V& y, const Elt& a, const V& x) const {
    return V{f_.addf(y.e, f_.mulf(a, x.e))};
  }
  V apy(const V& y, const Elt& a) const { return V{f_.addf(y.e, a)}; }

 private:
  const Field& f_;
  bool panic_on_assertion_failure_;
  mutable bool assertion_failed_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_EVALUATION_BACKEND_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_SUMCHECK_TESTING_H_
#define PRIVACY_PROOFS_ZK_LIB_SUMCHECK_TESTING_H_

#include <stddef.h>

#include <cstdint>
#include <memory>

#include "arrays/dense.h"
#include "random/transcript.h"
#include "sumcheck/circuit.h"
#include "sumcheck/prover.h"
#include "sumcheck/verifier.h"
#include "util/log.h"
#include "util/panic.h"

/*
These are methods that help test modules
by running the prover or the verifier.
*/
namespace proofs {
template <class Field>
void run_prover(const Circuit<Field> *C, std::unique_ptr<Dense<Field>> W,
                Proof<Field> *proof, const Field& F) {
  typename Prover<Field>::inputs pin;

  Prover<Field> prover(F);
  auto V = prover.eval_circuit(&pin, C, W->clone(), F);

  check(V != nullptr, "eval_circuit failed.");

  // Ensure the witness satisfies the circuit before making a proof.
  for (size_t i = 0; i < V->n1_; ++i) {
    if (V->v_[i] != F.zero()) {
      log(INFO, "witness failed: non-zero output at %zu", i);
    }
    check(V->v_[i] == F.zero(), "witness failed, non-zero output");
  }

  Transcript tsp((uint8_t *)"testing", 7);
  prover.prove(proof, nullptr, C, pin, tsp);
}

template <class Field>
void run_verifier(const Circuit<Field> *C, std::unique_ptr<Dense<Field>> W,
                  Proof<Field> &proof, const Field& F) {
  const char *why = "ok";
  auto V = std::make_unique<Dense<Field>>(F);
  Transcript tsv((uint8_t *)"testing", 7);
  check(Verifier<Field>::verify(&why, C, &proof, std::move(V),
                                     W->clone(), tsv, F), why);
}
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_SUMCHECK_TESTING_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_INTERPOLATION_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_INTERPOLATION_H_

#include <cstddef>

#include "algebra/poly.h"

namespace proofs {
// General-purpose polynomial interpolation routines,
// which operate on arbitrary points at the cost of
// computing inverses in the field.
// These static functions are grouped into a class due
// to the common template arguments.
template <size_t N, class Field>
class Interpolation {
 public:
  static const size_t kN = N;
  using Elt = typename Field::Elt;
  using PolyN = Poly<N, Field>;

  // Throughout, X are the evaluation points.

  // Lagrange basis to Newton
  static void newton_of_lagrange_inplace(PolyN &A, const PolyN &X,
                                         const Field &F) {
    // Cache one element E and its inverse.  In the common
    // case where the points X are in an arithmetic sequence,
    // this cache avoids the computation of most inverses.
    Elt e = F.one(), inve = F.one();

    for (size_t i = 1; i < N; i++) {
      for (size_t k = N; k-- > i;) {
        Elt dx = F.subf(X[k], X[k - i]);
        if (dx != e) {
          e = dx;
          inve = F.invertf(dx);
        }
        A[k] = F.mulf(F.subf(A[k], A[k - 1]), inve);
      }
    }
  }

  static PolyN newton_of_lagrange(const PolyN &L, const PolyN &X,
                                  const Field &F) {
    PolyN A = L;
    newton_of_lagrange_inplace(A, X, F);
    return A;
  }

  // evaluation in Newton basis
  static Elt eval_newton(PolyN &Newton, const PolyN &X, const Elt &x,
                         const Field &F) {
    Elt e{};

    for (size_t i = N; i-- > 0;) {
      e = F.addf(Newton[i], F.mulf(e, F.subf(x, X[i])));
    }
    return e;
  }

  // Newton basis to monomial basis (i.e., coefficients)
  static void monomial_of_newton_inplace(PolyN &A, const PolyN &X,
                                         const Field &F) {
    for (size_t i = N; i-- > 0;) {
      for (size_t k = i + 1; k < N; ++k) {
        A[k - 1] = F.subf(A[k - 1], F.mulf(A[k], X[i]));
      }
    }
  }

  static PolyN monomial_of_newton(const PolyN &Newton, const PolyN &X,
                                  const Field &F) {
    PolyN A = Newton;
    monomial_of_newton_inplace(A, X, F);
    return A;
  }

  // evaluation in the monomial basis
  static Elt eval_monomial(PolyN &M, const Elt &x, const Field &F) {
    Elt e{};

    for (size_t i = N; i-- > 0;) {
      e = F.addf(M[i], F.mulf(e, x));
    }
    return e;
  }

  static void monomial_of_lagrange_inplace(PolyN &A, const PolyN &X,
                                           const Field &F) {
    newton_of_lagrange_inplace(A, X, F);
    monomial_of_newton_inplace(A, X, F);
  }

  static PolyN monomial_of_lagrange(const PolyN &L, const PolyN &X,
                                    const Field &F) {
    PolyN A = L;
    monomial_of_lagrange_inplace(A, X, F);
    return A;
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_INTERPOLATION_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ZK_ZK_VERIFIER_H_
#define PRIVACY_PROOFS_ZK_LIB_ZK_ZK_VERIFIER_H_

#include <stddef.h>

#include <vector>

#include "arrays/dense.h"
#include "ligero/ligero_param.h"
#include "ligero/ligero_verifier.h"
#include "random/transcript.h"
#include "sumcheck/circuit.h"
#include "util/log.h"
#include "zk/zk_common.h"
#include "zk/zk_proof.h"

namespace proofs {
// ZK Verifier
//
// Verifies a zk proof. See note in the prover for the design.
// To verify a proof, instantiate the class, then call recv_commitment with
// the commitment, and finally call verify. It is possible to receive several
// commitments, or run other protocols between the recv_commitment and verify
// calls. This allows composing two proofs in parallel.
// To support this, the interface to both accepts a raw Transcript.
template <class Field, class RSFactory>
class ZkVerifier {
  using Elt = typename Field::Elt;

 public:
  explicit ZkVerifier(const Circuit<Field>& c, const RSFactory& rsf,
                      size_t rate, size_t nreq, const Field& F)
      : circ_(c),
        n_witness_(c.ninputs - c.npub_in),
        param_(n_witness_ + ZkCommon<Field>::pad_size(c), c.nl, rate, nreq),
        lqc_(c.nl),
        rsf_(rsf),
        f_(F) {
    ZkCommon<Field>::setup_lqc(c, lqc_, n_witness_);
  }

  void recv_commitment(const ZkProof<Field>& zk, Transcript& t) const {
    log(INFO, "verifier: recv commit");
    LigeroVerifier<Field, RSFactory>::receive_commitment(zk.com, t);
  }

  // Verifies the proof.
  bool verify(const ZkProof<Field>& zk, const Dense<Field>& pub,
              Transcript& tv) const {
    log(INFO, "verifier: verify");

    ZkCommon<Field>::initialize_sumcheck_fiat_shamir(tv, circ_, pub, f_);

    // Derive constraints on the witness.
    using Llc = LigeroLinearConstraint<Field>;
    std::vector<Llc> A;
    std::vector<Elt> b;
    const LigeroHash hash_of_A{0xde, 0xad, 0xbe, 0xef};
    size_t cn = ZkCommon<Field>::verifier_constraints(circ_, pub, zk.proof,
                                                      /*aux=*/nullptr, A, b, tv,
                                                      n_witness_, f_);

    const char* why = "";
    bool ok = LigeroVerifier<Field, RSFactory>::verify(
        &why, param_, zk.com, zk.com_proof, tv, cn, A.size(), &A[0], hash_of_A,
        &b[0], &lqc_[0], rsf_, f_);

    log(INFO, "verify done: %s", why);
    return ok;
  }

 private:
  const Circuit<Field>& circ_;
  const size_t n_witness_;
  const LigeroParam<Field> param_;
  std::vector<LigeroQuadraticConstraint> lqc_;
  const RSFactory& rsf_;
  const Field& f_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ZK_ZK_VERIFIER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ARRAYS_EQS_H_
#define PRIVACY_PROOFS_ZK_LIB_ARRAYS_EQS_H_

#include <stddef.h>

#include <vector>

#include "algebra/blas.h"
#include "arrays/affine.h"
#include "arrays/dense.h"
#include "util/panic.h"

namespace proofs {

// Stateful implementation of EQ[I, j] which, for fixed
// I, holds an array indexed by j.
template <class Field>
class Eqs : public Dense<Field> {
  using Elt = typename Field::Elt;
  using Dense<Field>::v_;
  using Dense<Field>::n0_;

 public:
  Eqs(size_t logn, corner_t n, const Elt I[/*logn*/], const Field& F)
      : Dense<Field>(n, 1) {
    filleq(&v_[0], logn, n, I, F);
  }

  corner_t n() const { return n0_; }

  // Optimization for a special case: return a raw vector EQ[G0|.] + alpha *
  // EQ[G1|.]  Return std::vector<> because we don't need the full
  // dense<> machinery.
  static std::vector<Elt> raw_eq2(size_t logn, corner_t n, const Elt* G0,
                                  const Elt* G1, const Elt& alpha,
                                  const Field& F) {
    std::vector<Elt> eq0(n);
    std::vector<Elt> eq1(n);
    filleq(&eq0[0], logn, n, G0, F);
    filleq(&eq1[0], logn, n, G1, F);
    Blas<Field>::axpy(n, &eq0[0], 1, alpha, &eq1[0], 1, F);
    return eq0;
  }

 private:
  // Return ceil(a / 2^{n}) for a != 0.
  //
  // Several ways exist to compute ceil(a/b) given a primitive that
  // computes floor(a/b), such as the C++ unsigned division operator.
  // The simplest one is floor((a+(b-1))/b), which potentally overflows.
  // Another way is 1+floor((a-1)/b), which underflows for a==0 but
  // otherwise does not overflow.  More complicated ways exist that neither
  // overflow nor underflow.  Since the rest of the code assumes
  // a!=0 anyway, we use the 1+floor((a-1)/b) version.
  static corner_t ceilshr(corner_t a, size_t n) { return 1u + ((a - 1u) >> n); }

  // Compute the array EQ[Q, i] for all 0<=i<n, for n <= 2^{logn}.
  // (logn can otherwise be arbitrarily large.)
  //
  // Let Q be the array of field elements Q[0,logn), and let
  // i[l] be the l-th bit of the binary representation of i, for
  // 0 <= l < logn.
  //
  // We have
  //   EQ[Q, i] = (1 - Q[0]) * EQ[Q[1:], i[1:]]     if i[0] = 0;
  //   EQ[Q, i] =       Q[0] * EQ[Q[1:], i[1:]]     if i[0] = 1.
  //
  // Thus, EQ{n, logn} can be expressed in terms of EQ{ceil(n/2), logn-1}
  // of half the size.
  static void filleq(Elt* eq, size_t logn, corner_t n, const Elt* Q,
                     const Field& F) {
    check(n > 0, "n > 0");
    eq[0] = F.one();
    for (size_t l = logn; l-- > 0;) {
      corner_t nl = ceilshr(n, l);
      corner_t i = ceilshr(nl, 1);

      // Special case for the first iteration of the i-loop
      // below: don't compute eq[2*i+1] (post decrement) if it
      // would overflow the array.
      if (/*2*(i-1)+1 = */ 2 * i - 1 >= nl) {
        i--;
        Elt v = eq[i], qv = Q[l];
        F.mul(qv, v);
        eq[2 * i] = v;
        F.sub(eq[2 * i], qv);
      }
      while (i-- > 0) {
        // Assign
        //   eq[2*i]   = (1-Q[l])*eq[i]
        //   eq[2*i+1] = Q[l]*eq[i]
        // with one multiplication.
        Elt v = eq[i], qv = Q[l];
        F.mul(qv, v);
        eq[2 * i] = v;
        F.sub(eq[2 * i], qv);
        eq[2 * i + 1] = qv;
      }
    }
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ARRAYS_EQS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_ATTRIBUTE_IDS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_ATTRIBUTE_IDS_H_

#include <string_view>

namespace proofs {

struct MdocAttribute {
  std::string_view identifier;
  std::string_view documentspec;
};

// Extracted from
// https://github.com/ISOWG10/ISO-18013/blob/main/Working%20Documents/Working%20Draft%20WG%2010_N2549_ISO-IEC%2018013-5-%20Personal%20identification%20%E2%80%94%20ISO-compliant%20driving%20licence%20%E2%80%94%20Part%205-%20Mobile%20driving%20lic.pdf
// https://www.aamva.org/getmedia/bb4fee66-592d-4d39-813a-8fdfd910268a/MobileDLGuidelines1-5.pdf
constexpr MdocAttribute kMdocAttributes[] = {
    {"family_name", "org.iso.18013.5.1"},
    {"given_name", "org.iso.18013.5.1"},
    {"birth_date", "org.iso.18013.5.1"},
    {"issue_date", "org.iso.18013.5.1"},
    {"expiry_date", "org.iso.18013.5.1"},
    {"issuing_country", "org.iso.18013.5.1"},
    {"issuing_authority", "org.iso.18013.5.1"},
    {"document_number", "org.iso.18013.5.1"},
    {"portrait", "org.iso.18013.5.1"},
    {"driving_privileges", "org.iso.18013.5.1"},
    {"un_distinguishing_sign", "org.iso.18013.5.1"},
    {"administrative_number", "org.iso.18013.5.1"},
    {"sex", "org.iso.18013.5.1"},
    {"height", "org.iso.18013.5.1"},
    {"weight", "org.iso.18013.5.1"},
    {"eye_colour", "org.iso.18013.5.1"},
    {"hair_colour", "org.iso.18013.5.1"},
    {"birth_place", "org.iso.18013.5.1"},
    {"resident_address", "org.iso.18013.5.1"},
    {"portrait_capture_date", "org.iso.18013.5.1"},
    {"age_in_years", "org.iso.18013.5.1"},
    {"age_birth_year", "org.iso.18013.5.1"},
    {"age_over_10", "org.iso.18013.5.1"},
    {"age_over_11", "org.iso.18013.5.1"},
    {"age_over_12", "org.iso.18013.5.1"},
    {"age_over_13", "org.iso.18013.5.1"},
    {"age_over_14", "org.iso.18013.5.1"},
    {"age_over_15", "org.iso.18013.5.1"},
    {"age_over_16", "org.iso.18013.5.1"},
    {"age_over_17", "org.iso.18013.5.1"},
    {"age_over_18", "org.iso.18013.5.1"},
    {"age_over_19", "org.iso.18013.5.1"},
    {"age_over_20", "org.iso.18013.5.1"},
    {"age_over_21", "org.iso.18013.5.1"},
    {"age_over_23", "org.iso.18013.5.1"},
    {"age_over_25", "org.iso.18013.5.1"},
    {"age_over_50", "org.iso.18013.5.1"},
    {"age_over_55", "org.iso.18013.5.1"},
    {"age_over_60", "org.iso.18013.5.1"},
    {"age_over_65", "org.iso.18013.5.1"},
    {"age_over_70", "org.iso.18013.5.1"},
    {"age_over_75", "org.iso.18013.5.1"},
    {"issuing_jurisdiction", "org.iso.18013.5.1"},
    {"nationality", "org.iso.18013.5.1"},
    {"resident_city", "org.iso.18013.5.1"},
    {"resident_state", "org.iso.18013.5.1"},
    {"resident_postal_code", "org.iso.18013.5.1"},
    {"resident_country", "org.iso.18013.5.1"},
    {"biometric_template_face", "org.iso.18013.5.1"},
    {"biometric_template_voice", "org.iso.18013.5.1"},
    {"biometric_template_finger", "org.iso.18013.5.1"},
    {"biometric_template_iris", "org.iso.18013.5.1"},
    {"biometric_template_retina", "org.iso.18013.5.1"},
    {"biometric_template_hand_geometry", "org.iso.18013.5.1"},
    {"biometric_template_keystroke", "org.iso.18013.5.1"},
    {"biometric_template_signature_sign", "org.iso.18013.5.1"},
    {"biometric_template_lip_movement", "org.iso.18013.5.1"},
    {"biometric_template_thermal_face", "org.iso.18013.5.1"},
    {"biometric_template_thermal_hand", "org.iso.18013.5.1"},
    {"biometric_template_gait", "org.iso.18013.5.1"},
    {"biometric_template_body_odor", "org.iso.18013.5.1"},
    {"biometric_template_dna", "org.iso.18013.5.1"},
    {"biometric_template_ear", "org.iso.18013.5.1"},
    {"biometric_template_finger_geometry", "org.iso.18013.5.1"},
    {"biometric_template_palm_geometry", "org.iso.18013.5.1"},
    {"biometric_template_vein_pattern", "org.iso.18013.5.1"},
    {"biometric_template_foot_print", "org.iso.18013.5.1"},
    {"family_name_national_character", "org.iso.18013.5.1"},
    {"given_name_national_character", "org.iso.18013.5.1"},
    {"signature_usual_mark", "org.iso.18013.5.1"},

    {"name_suffix", "org.iso.18013.5.1.aamva"},
    {"organ_donor", "org.iso.18013.5.1.aamva"},
    {"veteran", "org.iso.18013.5.1.aamva"},
    {"family_name_truncation", "org.iso.18013.5.1.aamva"},
    {"given_name_truncation", "org.iso.18013.5.1.aamva"},
    {"aka_family_name.v2", "org.iso.18013.5.1.aamva"},
    {"aka_given_name.v2", "org.iso.18013.5.1.aamva"},
    {"aka_suffix", "org.iso.18013.5.1.aamva"},
    {"weight_range", "org.iso.18013.5.1.aamva"},
    {"race_ethnicity", "org.iso.18013.5.1.aamva"},
    {"sex", "org.iso.18013.5.1.aamva"},
    {"first_name", "org.iso.18013.5.1.aamva"},
    {"middle_names", "org.iso.18013.5.1.aamva"},
    {"first_name_truncation", "org.iso.18013.5.1.aamva"},
    {"middle_names_truncation", "org.iso.18013.5.1.aamva"},
    {"EDL_credential", "org.iso.18013.5.1.aamva"},
    {"EDL_credential.v2", "org.iso.18013.5.1.aamva"},
    {"DHS_compliance", "org.iso.18013.5.1.aamva"},
    {"resident_county", "org.iso.18013.5.1.aamva"},
    {"resident_county.v2", "org.iso.18013.5.1.aamva"},
    {"hazmat_endorsement_expiration_date", "org.iso.18013.5.1.aamva"},
    {"CDL_indicator", "org.iso.18013.5.1.aamva"},
    {"CDL_non_domiciled", "org.iso.18013.5.1.aamva"},
    {"CDL_non_domiciled.v2", "org.iso.18013.5.1.aamva"},
    {"DHS_compliance_text", "org.iso.18013.5.1.aamva"},
    {"DHS_temporary_lawful_status", "org.iso.18013.5.1.aamva"},
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_ATTRIBUTE_IDS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_TEST_ATTRIBUTES_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_TEST_ATTRIBUTES_H_

#include "circuits/mdoc/mdoc_zk.h"

namespace proofs {
namespace test {
static const RequestedAttribute age_over_18 = {
    {'a', 'g', 'e', '_', 'o', 'v', 'e', 'r', '_', '1', '8'},
    {0xf5},
    11,
    1,
    kPrimitive};

static const RequestedAttribute not_over_18 = {
    .id = {'a', 'g', 'e', '_', 'o', 'v', 'e', 'r', '_', '1', '8'},
    .value = {0xf4},
    .id_len = 11,
    .value_len = 1,
    .type = kPrimitive};

static const RequestedAttribute familyname_mustermann = {
    .id = {'f', 'a', 'm', 'i', 'l', 'y', '_', 'n', 'a', 'm', 'e'},
    .value = {'M', 'u', 's', 't', 'e', 'r', 'm', 'a', 'n', 'n'},
    .id_len = 11,
    .value_len = 10,
    .type = kString};

static const RequestedAttribute birthdate_1971_09_01 = {
    .id = {'b', 'i', 'r', 't', 'h', '_', 'd', 'a', 't', 'e'},
    .value = {'1', '9', '7', '1', '-', '0', '9', '-', '0', '1'},
    .id_len = 10,
    .value_len = 10,
    .type = kDate};

static const RequestedAttribute birthdate_1998_09_04 = {
    .id = {'b', 'i', 'r', 't', 'h', '_', 'd', 'a', 't', 'e'},
    .value = {'1', '9', '9', '8', '-', '0', '9', '-', '0', '4'},
    .id_len = 10,
    .value_len = 10,
    .type = kDate};

static const RequestedAttribute height_175 = {
    {'h', 'e', 'i', 'g', 'h', 't'}, {0x18, 0xaf}, 6, 2, kInt};

static const RequestedAttribute issue_date_2024_03_15 = {
    {'i', 's', 's', 'u', 'e', '_', 'd', 'a', 't', 'e'},
    {'2', '0', '2', '4', '-', '0', '3', '-', '1', '5'},
    10,
    10,
    kDate};

}  // namespace test
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_TEST_ATTRIBUTES_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_UTIL_CRYPTO_H_
#define PRIVACY_PROOFS_ZK_LIB_UTIL_CRYPTO_H_

// Encapsulates all of the cryptographic primitives used by this library.
// Specifically, for the collision-resistant hash function, this library uses
// SHA256. For a pseudo-random function, this library uses AES in ECB mode.
// Finally, this library provides a method to generate random bytes using the
// openssl library.

#include <cstddef>
#include <cstdint>
#include <cstring>

#include "util/panic.h"
#include "openssl/sha.h"
#include "openssl/evp.h"
#include "openssl/aes.h"

namespace proofs {

constexpr size_t kSHA256DigestSize = 32;
constexpr size_t kPRFKeySize = 32;
constexpr size_t kPRFInputSize = 16;
constexpr size_t kPRFOutputSize = 16;

class SHA256 {
 public:
  SHA256() { SHA256_Init(&sha_); }

  // Disable copy for good measure.
  SHA256(const SHA256&) = delete;
  SHA256& operator=(const SHA256&) = delete;

  void Update(const uint8_t bytes[/*n*/], size_t n) { SHA256_Update(&sha_, bytes, n); }
  void DigestData(uint8_t digest[/* kSHA256DigestSize */]) {
    SHA256_Final(digest, &sha_);
  }
  void CopyState(const SHA256& src) { sha_ = src.sha_; }

  void Update8(uint64_t x) {
    uint8_t buf[8];
    for (size_t i = 0; i < 8; ++i) {
      buf[i] = x & 0xff;
      x >>= 8;
    }
    Update(buf, 8);
  }

 private:
  SHA256_CTX sha_;
};

// A pseudo-random function interface. This implementation uses AES in ECB mode.
// The caller must ensure that arguments are not reused.
class PRF {
 public:
  explicit PRF(const uint8_t key[/*kPRFKeySize*/]) {
    ctx_ = EVP_CIPHER_CTX_new();
    int ret =
        EVP_EncryptInit_ex(ctx_, EVP_aes_256_ecb(), nullptr, key, nullptr);
    check(ret == 1, "EVP_EncryptInit_ex failed");
  }

  ~PRF() { EVP_CIPHER_CTX_free(ctx_); }

  // Disable copy for good measure.
  PRF(const PRF&) = delete;
  PRF& operator=(const PRF&) = delete;

  // Evaluate the PRF on the input and write the output to the output buffer.
  // This method should only be used internally by the Transcript class. The
  // caller must ensure that the input and output buffers are different.
  // This function implements a permutation, but we only need to exploit its
  // pseudo-random function property in this application.
  void Eval(uint8_t out[/*kPRFOutputSize*/], uint8_t in[/*kPRFInputSize*/]) {
    int out_len = static_cast<int>(kPRFOutputSize);
    int ret = EVP_EncryptUpdate(ctx_, out, &out_len, in,
                                static_cast<int>(kPRFInputSize));
    check(ret == 1, "EVP_EncryptUpdate failed");
  }

 private:
  EVP_CIPHER_CTX* ctx_;
};

// Generate n random bytes, following the openssl API convention.
// This method will panic if the openssl library fails.
void rand_bytes(uint8_t out[/*n*/], size_t n);

void hex_to_str(char out[/* 2*n + 1*/], const uint8_t in[/*n*/], size_t n);

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_UTIL_CRYPTO_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_EXAMPLES_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_EXAMPLES_H_

#include <cstddef>
#include <cstdint>

#include "algebra/static_string.h"

namespace proofs {

struct SmallTest {
  StaticString pkx, pky; /* public key of the issuer */
  StaticString sigr, sigs, sigtr, sigts;
  uint8_t transcript[1024];
  size_t transcript_size;
  uint8_t *now;
  size_t mdoc_size;
  uint8_t mdoc[5000];
};

static const struct SmallTest mdoc_small_tests[] = {
    // Example that requires 3 SHA blocks for the credential.
    {StaticString(
         "0x298aa30b14298b8bdb5b2b1cc0150e84a86a1469fa813f8df0f0bd09d8489661"),
     StaticString(
         "0x2f821d00395d0c63b222a8613c7af5f5cb2b655dda6c3926af7ea3acd6a05fba"),
     StaticString(
         "0xc96b7605f15536877592c24d4d65066a377c34e3e6a624ea52ce9d899207"),
     StaticString(
         "0xaca1128a3460368caefc5c24a27a7f9e8cc8d590b93485db2c408d1ac96d80ae"),
     StaticString(
         "0x08ca78156b6c60c711e501641d4b5000f1ff7715386f1186ce4aec1d6eb2ba92"),
     StaticString(
         "0xfdcb8d4da84f1dc03a7bd22909a6bcc103281310f9fe1fb97c13e5d2eb6934df"),
     {0x60, 0x80, 0xf3, 0x09, 0x55, 0x56, 0xe5, 0x21, 0x4c, 0x5f, 0xff,
      0x7c, 0x6d, 0x3d, 0xcb, 0x90, 0x3e, 0x2d, 0xcc, 0x45, 0x42, 0x9c,
      0x4d, 0x81, 0xf9, 0x80, 0x8d, 0x5e, 0x7a, 0xd2, 0x14, 0x50},
     32,
     (uint8_t *)"20241005",
     183,
     {0x45, 0x72, 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x75, 0x73, 0x74,
      0x65, 0x72, 0x6d, 0x61, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x31, 0x39, 0x38, 0x30, 0x30, 0x31, 0x30, 0x31,
      0x00, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0x00, 0x00, 0x00, 0x00,
      0x32, 0x30, 0x32, 0x34, 0x31, 0x30, 0x30, 0x31, 0x32, 0x30, 0x32, 0x35,
      0x31, 0x30, 0x30, 0x31, 0x3c, 0xeb, 0x4a, 0xec, 0x0d, 0x76, 0xbb, 0x50,
      0x35, 0x09, 0xc6, 0x46, 0xad, 0xc6, 0x30, 0x3f, 0xb9, 0xea, 0xa5, 0x84,
      0xb4, 0x8f, 0xa0, 0xbe, 0x07, 0x6f, 0x14, 0x28, 0xe4, 0xec, 0x3b, 0x37,
      0x77, 0x91, 0x61, 0xbe, 0xb9, 0x86, 0x90, 0xfb, 0xc8, 0xe3, 0x57, 0x5a,
      0x9d, 0xd9, 0x41, 0x91, 0xae, 0x8e, 0x4d, 0xbe, 0x2c, 0x44, 0x39, 0x07,
      0x69, 0xea, 0x0c, 0x08, 0x80, 0x22, 0xf2, 0xc6, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00}},
    // The next examples requires 4 SHA blocks for the credential.
    {
        StaticString("0x08a25115e57d680260aafa44e674d45d43af885fffa6746b44ece71"
                     "cd42febd2"),
        StaticString("0x4a177f6c38543876c66573218db63de627d343f16a483a313069830"
                     "37df79b2f"),
        StaticString("0x95394343021474ed688e7e783063c768a3343def0f1245f1da09406"
                     "34eb57410"),
        StaticString("0x4953b2f11cd46c8e1673ac50ddc51665702a27e5788eca3a8bffa67"
                     "fbdbf65ce"),
        StaticString("0x1ca6eef3238ec5320429774f6b6b743cc868b3cfebff96c41112b36"
                     "5638a9dc5"),
        StaticString("0x7e303bd730d0e663621ddcaf52991ba9f744cabe161598a44b4508f"
                     "de7bdaf2d"),

        {0x60, 0x80, 0xf3, 0x09, 0x55, 0x56, 0xe5, 0x21, 0x4c, 0x5f, 0xff,
         0x7c, 0x6d, 0x3d, 0xcb, 0x90, 0x3e, 0x2d, 0xcc, 0x45, 0x42, 0x9c,
         0x4d, 0x81, 0xf9, 0x80, 0x8d, 0x5e, 0x7a, 0xd2, 0x14, 0x50},
        32,
        (uint8_t *)"20241005",
        247,
        {0x45, 0x72, 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x75, 0x73, 0x74,
         0x65, 0x72, 0x6d, 0x61, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x31, 0x39, 0x38, 0x30, 0x30, 0x31, 0x30, 0x31,
         0x00, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0x00, 0x00, 0x00, 0x00,
         0x32, 0x30, 0x32, 0x34, 0x31, 0x30, 0x30, 0x31, 0x32, 0x30, 0x32, 0x35,
         0x31, 0x30, 0x30, 0x31, 0x3e, 0xf6, 0x39, 0x05, 0x33, 0xf8, 0xf3, 0x37,
         0x7d, 0xe0, 0xb2, 0xb5, 0x65, 0x19, 0x93, 0x3f, 0x49, 0xe1, 0xa1, 0x41,
         0x57, 0xa5, 0x22, 0x0e, 0xac, 0x79, 0x8a, 0xe2, 0xe2, 0xea, 0xd2, 0x0c,
         0x9f, 0x23, 0x3d, 0x1b, 0xe2, 0x8e, 0x69, 0xc1, 0xda, 0x76, 0xec, 0xf9,
         0xc3, 0x0c, 0xf9, 0x3b, 0xb5, 0xb6, 0x69, 0x6f, 0xc6, 0xc2, 0x8a, 0xa8,
         0x98, 0x19, 0x7d, 0xbf, 0xea, 0x6d, 0xad, 0x40, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
    },
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_EXAMPLES_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_UTIL_READBUFFER_H_
#define PRIVACY_PROOFS_ZK_LIB_UTIL_READBUFFER_H_

#include <cstddef>
#include <cstdint>
#include <vector>

#include "util/panic.h"

namespace proofs {

class ReadBuffer {
 public:
  explicit ReadBuffer(const uint8_t *buf, size_t sz)
      : buf_(buf), size_(sz), next_(0) {}

  explicit ReadBuffer(const std::vector<uint8_t> &v)
      : ReadBuffer(v.data(), v.size()) {}

  // no copies
  ReadBuffer(const ReadBuffer &) = delete;

  // TRUE if at least N bytes remain
  bool have(size_t n) const { return remaining() >= n; }

  size_t remaining() const {
    check(next_ <= size_, "next_ <= size_");
    return size_ - next_;
  }

  const uint8_t *next(size_t n) {
    check(have(n), "have(n)");
    const uint8_t *p = &buf_[next_];
    next_ += n;
    return p;
  }

  void next(size_t n, uint8_t dest[/*n*/]) {
    const uint8_t *p = next(n);
    for (size_t i = 0; i < n; ++i) {
      dest[i] = p[i];
    }
  }

 private:
  const uint8_t *buf_;
  size_t size_;
  size_t next_;
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_UTIL_READBUFFER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_RANDOM_RANDOM_H_
#define PRIVACY_PROOFS_ZK_LIB_RANDOM_RANDOM_H_

#include <cstdint>
#include <cstdlib>
#include <optional>
#include <utility>
#include <vector>

#include "util/panic.h"

namespace proofs {

// Our protocols require random coins; this interface provides both prover
// and verifier components with those coins. Re-implementing this interface
// allows easily supporting the Fiat-Shamir transform, or for sampling using
// a system provided RNG such as openssl.
class RandomEngine {
 public:
  virtual ~RandomEngine() = default;
  virtual void bytes(uint8_t* buf, size_t n) = 0;  // pure virtual

  // Sample a random field element.
  // TODO [matteof 2025-02-07] Per RFC, we must mask off the high
  // bits, but this requires changes to the field interface.
  // Punt for now since the mask is all ones anyway.
  template <class Field>
  typename Field::Elt elt(const Field& F) {
    // Expected constant time.
    uint8_t buf[Field::kBytes];
    for (;;) {
      bytes(buf, sizeof(buf));
      if (std::optional<typename Field::Elt> maybe = F.of_bytes_field(buf)) {
        return maybe.value();
      }
    }
  }

  template <class Field>
  typename Field::Elt subfield_elt(const Field& F) {
    // Expected constant time.
    uint8_t buf[Field::kSubFieldBytes];
    for (;;) {
      bytes(buf, sizeof(buf));
      if (std::optional<typename Field::Elt> maybe = F.of_bytes_subfield(buf)) {
        return maybe.value();
      }
    }
  }

  // Convenience method to sample an array of random field elements.
  template <class Field>
  void elt(typename Field::Elt e[/*n*/], size_t n, const Field& F) {
    for (size_t i = 0; i < n; ++i) e[i] = elt(F);
  }

  // the minimal bitmask such that (n & mask) == n
  size_t mask(size_t n) {
    size_t mask = 0;
    while ((n & mask) != n) {
      mask <<= 1;
      mask |= 1u;
    }
    return mask;
  }

  // random size_t < n
  size_t nat(size_t n) {
    check(n > 0, "nat(0)");

    // compute the minimum number of random bytes needed
    size_t l = 0;
    size_t nn = n;
    while (nn != 0) {
      nn >>= 8;
      ++l;
    }
    check(l <= sizeof(size_t), "l <= sizeof(size_t)");

    size_t msk = mask(n);
    size_t r;
    uint8_t buf[sizeof(size_t)];

    // rejection sampling
    do {
      // consume L random bytes
      bytes(buf, l);

      // little-endian read
      r = 0;
      for (size_t i = l; i-- > 0;) {
        r = (r << 8) | buf[i];
      }

      // mask off high bits
      r &= msk;
    } while (r >= n);

    return r;
  }

  // Choose K distinct random naturals in [0..N).
  // Textbook algorithm requiring O(N) space
  void choose(size_t res[/*k*/], size_t n, size_t k) {
    check(n >= k, "n >= k");

    std::vector<size_t> A(n);
    for (size_t i = 0; i < n; ++i) {
      A[i] = i;
    }
    for (size_t i = 0; i < k; ++i) {
      size_t j = i + nat(n - i);
      std::swap(A[i], A[j]);
      res[i] = A[i];
    }
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_RANDOM_RANDOM_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FFT_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FFT_H_

#include <stddef.h>
#include <stdint.h>

#include "algebra/permutations.h"
#include "algebra/twiddle.h"

namespace proofs {
/*
Fast Fourier Transform (FFT).

We use FFTPACK/FFTW/MATLAB conventions where the FFT
has a negative sign in the exponent.  For root of unity
W, input ("time") T and output ("frequency") F, the
"forward" FFT computes

    F[k] = SUM_{j} T[j] W^{-jk}

and the "backward" fft computes

    T[j] = SUM_{k} F[k] W^{jk}

A forward transform followed by a backward transform
multiplies the array by N.

Matlab and engineers call the forward transform the FFT.
Mathematicians tend to call the backward transform the FFT.
*/
template <class Field>
class FFT {
  using Elt = typename Field::Elt;

  static void butterfly(Elt* A, size_t s, const Field& F) {
    Elt t = A[s];
    A[s] = A[0];
    F.add(A[0], t);
    F.sub(A[s], t);
  }

  static void butterflytw(Elt* A, size_t s, const Elt& twiddle,
                          const Field& F) {
    Elt t = A[s];
    F.mul(t, twiddle);
    A[s] = A[0];
    F.add(A[0], t);
    F.sub(A[s], t);
  }

 public:
  // Backward FFT.
  // N (the length of A) must be a power of 2
  static void fftb(Elt A[/*n*/], size_t n, const Elt& omega,
                   uint64_t omega_order, const Field& F) {
    if (n <= 1) {
      return;
    }

    Elt omega_n = Twiddle<Field>::reroot(omega, omega_order, n, F);
    Twiddle<Field> roots(n, omega_n, F);

    Permutations<Elt>::bitrev(A, n);

    // m=1 iteration
    for (size_t k = 0; k < n; k += 2) {
      butterfly(&A[k], 1, F);
    }

    // m>1 iterations
    for (size_t m = 2; m < n; m = 2 * m) {
      size_t ws = n / (2 * m);
      for (size_t k = 0; k < n; k += 2 * m) {
        butterfly(&A[k], m, F);  // j==0
        for (size_t j = 1; j < m; ++j) {
          butterflytw(&A[k + j], m, roots.w_[j * ws], F);
        }
      }
    }
  }

  // forward transform
  static void fftf(Elt A[/*n*/], size_t n, const Elt& omega,
                   uint64_t omega_order, const Field& F) {
    fftb(A, n, F.invertf(omega), omega_order, F);
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FFT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_GF2K_SYSDEP_H_
#define PRIVACY_PROOFS_ZK_LIB_GF2K_SYSDEP_H_

#include <stddef.h>
#include <stdint.h>

#include <array>

// Hardcoded GF(2^128) SIMD arithmetic where
// GF(2^128) = GF(2)[x] / (x^128 + x^7 + x^2 + x + 1)

#if defined(__x86_64__) || defined(__i386__)
#include <immintrin.h>  // IWYU pragma: keep

namespace proofs {

using gf2_128_elt_t = __m128i;

static inline std::array<uint64_t, 2> uint64x2_of_gf2_128(gf2_128_elt_t x) {
  return std::array<uint64_t, 2>{static_cast<uint64_t>(x[0]),
                                 static_cast<uint64_t>(x[1])};
}

static inline gf2_128_elt_t gf2_128_of_uint64x2(
    const std::array<uint64_t, 2> &x) {
  // Cast to long long (as opposed to int64_t) is necessary because __m128i is
  // defined in terms of long long.
  return gf2_128_elt_t{static_cast<long long>(x[0]),
                       static_cast<long long>(x[1])};
}

static inline gf2_128_elt_t gf2_128_add(gf2_128_elt_t x, gf2_128_elt_t y) {
  return _mm_xor_si128(x, y);
}

// return t0 + x^64 * t1
static inline gf2_128_elt_t gf2_128_reduce(gf2_128_elt_t t0, gf2_128_elt_t t1) {
  const gf2_128_elt_t poly = {0x87};
  t0 = _mm_xor_si128(t0, _mm_slli_si128(t1, 64 /*bits*/ / 8 /*bits/byte*/));
  t0 = _mm_xor_si128(t0, _mm_clmulepi64_si128(t1, poly, 0x01));
  return t0;
}
static inline gf2_128_elt_t gf2_128_mul(gf2_128_elt_t x, gf2_128_elt_t y) {
  gf2_128_elt_t t1a = _mm_clmulepi64_si128(x, y, 0x01);
  gf2_128_elt_t t1b = _mm_clmulepi64_si128(x, y, 0x10);
  gf2_128_elt_t t1 = gf2_128_add(t1a, t1b);
  gf2_128_elt_t t2 = _mm_clmulepi64_si128(x, y, 0x11);
  t1 = gf2_128_reduce(t1, t2);
  gf2_128_elt_t t0 = _mm_clmulepi64_si128(x, y, 0x00);
  t0 = gf2_128_reduce(t0, t1);
  return t0;
}
}  // namespace proofs
#elif defined(__aarch64__)
//
// Implementation for arm/neon with AES instructions.
// We assume that __aarch64__ implies AES, which isn't necessarily
// the case.  If this is a problem, change the defined(__aarch64__)
// above and the code will fall back to the non-AES implementation
// below.
//
#include <arm_neon.h>  // IWYU pragma: keep

namespace proofs {
using gf2_128_elt_t = poly64x2_t;

static inline std::array<uint64_t, 2> uint64x2_of_gf2_128(gf2_128_elt_t x) {
  return std::array<uint64_t, 2>{static_cast<uint64_t>(x[0]),
                                 static_cast<uint64_t>(x[1])};
}

static inline gf2_128_elt_t gf2_128_of_uint64x2(
    const std::array<uint64_t, 2> &x) {
  return gf2_128_elt_t{static_cast<poly64_t>(x[0]),
                       static_cast<poly64_t>(x[1])};
}

static inline gf2_128_elt_t vmull_low(gf2_128_elt_t t0, gf2_128_elt_t t1) {
  poly64_t tt0 = vgetq_lane_p64(t0, 0);
  poly64_t tt1 = vgetq_lane_p64(t1, 0);
  return vreinterpretq_p64_p128(vmull_p64(tt0, tt1));
}
static inline gf2_128_elt_t vmull_high(gf2_128_elt_t t0, gf2_128_elt_t t1) {
  return vreinterpretq_p64_p128(vmull_high_p64(t0, t1));
}

// return t0 + x^64 * t1
static inline gf2_128_elt_t gf2_128_reduce(gf2_128_elt_t t0, gf2_128_elt_t t1) {
  const gf2_128_elt_t poly = {0x0, 0x87};
  const gf2_128_elt_t zero = {0x0, 0x0};
  t0 = vaddq_p64(t0, vextq_p64(zero, t1, 1));
  t0 = vaddq_p64(t0, vmull_high(t1, poly));
  return t0;
}
static inline gf2_128_elt_t gf2_128_add(gf2_128_elt_t x, gf2_128_elt_t y) {
  return vaddq_p64(x, y);
}
static inline gf2_128_elt_t gf2_128_mul(gf2_128_elt_t x, gf2_128_elt_t y) {
  gf2_128_elt_t swx = vextq_p64(x, x, 1);
  gf2_128_elt_t t1a = vmull_high(swx, y);
  gf2_128_elt_t t1b = vmull_low(swx, y);
  gf2_128_elt_t t1 = vaddq_p64(t1a, t1b);
  gf2_128_elt_t t2 = vmull_high(x, y);
  t1 = gf2_128_reduce(t1, t2);
  gf2_128_elt_t t0 = vmull_low(x, y);
  t0 = gf2_128_reduce(t0, t1);
  return t0;
}
}  // namespace proofs

#elif defined(__arm__) || defined(__aarch64__)
//
// Implementation for arm/neon without AES instructions
//
#include <arm_neon.h>  // IWYU pragma: keep

namespace proofs {
using gf2_128_elt_t = poly64x2_t;

static inline std::array<uint64_t, 2> uint64x2_of_gf2_128(gf2_128_elt_t x) {
  return std::array<uint64_t, 2>{static_cast<uint64_t>(x[0]),
                                 static_cast<uint64_t>(x[1])};
}

static inline gf2_128_elt_t gf2_128_of_uint64x2(
    const std::array<uint64_t, 2> &x) {
  return gf2_128_elt_t{static_cast<poly64_t>(x[0]),
                       static_cast<poly64_t>(x[1])};
}

static inline gf2_128_elt_t gf2_128_add(gf2_128_elt_t x, gf2_128_elt_t y) {
  return vaddq_p64(x, y);
}

// Emulate vmull_p64() with vmull_p8().
//
// This emulation is pretty naive and it performs a lot of permutations.
//
// A possibly better alternative appears in Danilo Câmara, Conrado
// Gouvêa, Julio López, Ricardo Dahab, "Fast Software Polynomial
// Multiplication on ARM Processors Using the NEON Engine", 1st
// Cross-Domain Conference and Workshop on Availability, Reliability,
// and Security in Information Systems (CD-ARES), Sep 2013,
// Regensburg, Germany. pp.137-154. ⟨hal-01506572⟩
//
// However, the code from that paper makes heavy use of type
// punning of 128-bit registers as two 64-bit registers, which
// I don't know how to express in C.
static inline poly8x16_t pmul64x8(poly8x8_t x, poly8_t y) {
  const poly8x16_t zero{};
  poly8x16_t prod = vmull_p8(x, vdup_n_p8(y));
  poly8x16x2_t uzp = vuzpq_p8(prod, zero);
  return vaddq_p8(uzp.val[0], vextq_p8(uzp.val[1], uzp.val[1], 15));
}

// multiply/add.  Return (cout, s) = cin + x * y where the final sum
// would be (cout << 8) + s.
static inline poly8x16x2_t pmac64x8(poly8x16_t cin, poly8x8_t x, poly8_t y) {
  const poly8x16_t zero{};
  poly8x16_t prod = vmull_p8(x, vdup_n_p8(y));
  poly8x16x2_t uzp = vuzpq_p8(prod, zero);
  uzp.val[0] = vaddq_p8(uzp.val[0], cin);
  return uzp;
}

static inline poly8x16_t pmul64x64(poly8x8_t x, poly8x8_t y) {
  poly8x16_t r{};

  poly8x16x2_t prod = pmac64x8(r, x, y[0]);
  r = prod.val[0];

  prod = pmac64x8(prod.val[1], x, y[1]);
  r = vaddq_p8(r, vextq_p8(prod.val[0], prod.val[0], 15));

  prod = pmac64x8(prod.val[1], x, y[2]);
  r = vaddq_p8(r, vextq_p8(prod.val[0], prod.val[0], 14));

  prod = pmac64x8(prod.val[1], x, y[3]);
  r = vaddq_p8(r, vextq_p8(prod.val[0], prod.val[0], 13));

  prod = pmac64x8(prod.val[1], x, y[4]);
  r = vaddq_p8(r, vextq_p8(prod.val[0], prod.val[0], 12));

  prod = pmac64x8(prod.val[1], x, y[5]);
  r = vaddq_p8(r, vextq_p8(prod.val[0], prod.val[0], 11));

  prod = pmac64x8(prod.val[1], x, y[6]);
  r = vaddq_p8(r, vextq_p8(prod.val[0], prod.val[0], 10));

  prod = pmac64x8(prod.val[1], x, y[7]);
  r = vaddq_p8(r, vextq_p8(prod.val[0], prod.val[0], 9));
  r = vaddq_p8(r, vextq_p8(prod.val[1], prod.val[1], 8));

  return r;
}

static inline gf2_128_elt_t vmull_low(gf2_128_elt_t t0, gf2_128_elt_t t1) {
  // vreinterpretq_p64_p8() seems not to be defined, use
  // static_cast<poly64x2_t>
  return static_cast<poly64x2_t>(pmul64x64(vget_low_p8(t0), vget_low_p8(t1)));
}
static inline gf2_128_elt_t vmull_high(gf2_128_elt_t t0, gf2_128_elt_t t1) {
  return static_cast<poly64x2_t>(pmul64x64(vget_high_p8(t0), vget_high_p8(t1)));
}

// vextq_p64() seems not to be defined.
static inline gf2_128_elt_t vextq_p64_1_emul(gf2_128_elt_t t0,
                                             gf2_128_elt_t t1) {
  return static_cast<poly64x2_t>(
      vextq_p8(static_cast<poly8x16_t>(t0), static_cast<poly8x16_t>(t1), 8));
}

// return t0 + x^64 * t1
static inline gf2_128_elt_t gf2_128_reduce(gf2_128_elt_t t0, gf2_128_elt_t t1) {
  const poly8_t poly = static_cast<poly8_t>(0x87);
  const gf2_128_elt_t zero = {0x0, 0x0};
  t0 = vaddq_p64(t0, vextq_p64_1_emul(zero, t1));
  t0 = vaddq_p64(t0, pmul64x8(vget_high_p8(t1), poly));
  return t0;
}

static inline gf2_128_elt_t gf2_128_mul(gf2_128_elt_t x, gf2_128_elt_t y) {
  gf2_128_elt_t swx = vextq_p64_1_emul(x, x);
  gf2_128_elt_t t1a = vmull_high(swx, y);
  gf2_128_elt_t t1b = vmull_low(swx, y);
  gf2_128_elt_t t1 = vaddq_p64(t1a, t1b);
  gf2_128_elt_t t2 = vmull_high(x, y);
  t1 = gf2_128_reduce(t1, t2);
  gf2_128_elt_t t0 = vmull_low(x, y);
  t0 = gf2_128_reduce(t0, t1);
  return t0;
}

}  // namespace proofs
#else
#error "unimplemented gf2k/sysdep.h"
#endif

#endif  // PRIVACY_PROOFS_ZK_LIB_GF2K_SYSDEP_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_PERMUTATIONS_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_PERMUTATIONS_H_

#include <stddef.h>

#include <utility>

namespace proofs {

template <class Elt>
class Permutations {
 public:
  static void bitrev(Elt A[/*n*/], size_t n) {
    size_t revi = 0;
    for (size_t i = 0; i < n - 1; ++i) {
      if (i < revi) {
        std::swap(A[i], A[revi]);
      }

      bitrev_increment(&revi, n);
    }
  }

 private:
  static void bitrev_increment(size_t* j, size_t bit) {
    do {
      bit >>= 1;
      *j ^= bit;
    } while (!(*j & bit));
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_PERMUTATIONS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_BLAS_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_BLAS_H_

// basic linear algebra subroutines
#include <stddef.h>

namespace proofs {
template <class Field>
class Blas {
 public:
  using Elt = typename Field::Elt;

  // SUM_{i} x[i * incx].y[i * incy]
  static Elt dot(size_t n, const Elt x[/*n:incx*/], size_t incx,
                 const Elt y[/*n:incy*/], size_t incy, const Field& F) {
    Elt r = F.zero();
    for (size_t i = 0; i < n; i++) {
      F.add(r, F.mulf(x[i * incx], y[i * incy]));
    }
    return r;
  }

  // SUM_{i} x[i * incx], or the dot product x^T * 1
  static Elt dot1(size_t n, const Elt x[/*n:incx*/], size_t incx,
                  const Field& F) {
    Elt r = F.zero();
    for (size_t i = 0; i < n; ++i) {
      F.add(r, x[i * incx]);
    }
    return r;
  }

  // y = a*y
  static void scale(size_t n, Elt y[/*k:incy*/], size_t incy, const Elt a,
                    const Field& F) {
    for (size_t i = 0; i < n; i++) {
      F.mul(y[i * incy], a);
    }
  }

  // y = a*x + y.
  static void axpy(size_t n, Elt y[/*k:incy*/], size_t incy, const Elt a,
                   const Elt x[/*k:incx*/], size_t incx, const Field& F) {
    for (size_t i = 0; i < n; i++) {
      F.add(y[i * incy], F.mulf(x[i * incx], a));
    }
  }

  // nonstandard axpy() where A[] is itself an array
  static void vaxpy(size_t n, Elt y[/*k:incy*/], size_t incy,
                    const Elt a[/*k:inca*/], size_t inca,
                    const Elt x[/*k:incx*/], size_t incx, const Field& F) {
    for (size_t i = 0; i < n; i++) {
      F.add(y[i * incy], F.mulf(x[i * incx], a[i * inca]));
    }
  }

  // y[i] -= a[i] * x[i]
  static void vymax(size_t n, Elt y[/*k:incy*/], size_t incy,
                    const Elt a[/*k:inca*/], size_t inca,
                    const Elt x[/*k:incx*/], size_t incx, const Field& F) {
    for (size_t i = 0; i < n; i++) {
      F.sub(y[i * incy], F.mulf(x[i * incx], a[i * inca]));
    }
  }

  static bool equal(size_t n, const Elt x[/*n:incx*/], size_t incx,
                    const Elt y[/*n:incy*/], size_t incy, const Field& F) {
    for (size_t i = 0; i < n; i++) {
      if (x[i * incx] != y[i * incy]) return false;
    }
    return true;
  }

  static bool equal0(size_t n, const Elt x[/*n:incx*/], size_t incx,
                     const Field& F) {
    for (size_t i = 0; i < n; i++) {
      if (x[i * incx] != F.zero()) return false;
    }
    return true;
  }

  static void copy(size_t n, Elt dst[/*n:incx*/], size_t incd,
                   const Elt src[/*n:incy*/], size_t incs) {
    for (size_t i = 0; i < n; i++) {
      dst[i * incd] = src[i * incs];
    }
  }

  // DST[i] = SRC[IDX[i]].  DST and SRC must not overlap.
  static void gather(size_t n, Elt dst[/*n*/], const Elt src[],
                     const size_t idx[/*n*/]) {
    for (size_t i = 0; i < n; i++) {
      dst[i] = src[idx[i]];
    }
  }

  static void clear(size_t n, Elt dst[/*n:incx*/], size_t incd,
                    const Field& F) {
    for (size_t i = 0; i < n; i++) {
      dst[i * incd] = F.zero();
    }
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_BLAS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_SHA256_TEST_VALUES_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_SHA256_TEST_VALUES_H_

#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>

namespace proofs {

struct sha256_testvec {
  const char* str;
  size_t len;
  uint8_t hash[32];
};

// A set of SHA256 test vectors used to verify the circuit implementation.
static const struct sha256_testvec SHA256_TV[] = {
    {"", 0, {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
             0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
             0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}},
    {"abc", 3, {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
                0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
                0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
                0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}},
    {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
     56,
     {0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26,
      0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff,
      0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1}},
    {"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmno"
     "pjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
     112,
     {0xcf, 0x5b, 0x16, 0xa7, 0x78, 0xaf, 0x83, 0x80, 0x03, 0x6c, 0xe5,
      0x9e, 0x7b, 0x04, 0x92, 0x37, 0x0b, 0x24, 0x9b, 0x11, 0xe8, 0xf0,
      0x7a, 0x51, 0xaf, 0xac, 0x45, 0x03, 0x7a, 0xfe, 0xe9, 0xd1}},
    {"D818590293A66776657273696F6E63312E306F646967657374416C676F726974686D67534"
     "8412D32353667646F6354797065756F72672E69736F2E31383031332E352E312E6D444C6C"
     "76616C756544696765737473A2716F72672E69736F2E31383031332E352E31A3005820CF9"
     "C1CB89584BF8C4176A37C2C954A8DC56077D3BA65EE44011E62AB7C63CE2D0158202F00C7"
     "0D5FA9867D7BD2207E0D0B87E35A9AC962A8DE36EE1BE3944B63B39141025820EA9C0339A"
     "AF9BAE8",
     372,
     {0xc7, 0xce, 0x90, 0x99, 0xbd, 0xb6, 0x41, 0x75, 0x02, 0xb7, 0x3e,
      0x44, 0xc9, 0x82, 0x7c, 0xd2, 0x95, 0x6c, 0x54, 0x11, 0x0a, 0x39,
      0xb2, 0x60, 0x67, 0xfb, 0xf7, 0x9f, 0xf8, 0x9b, 0x20, 0xee}},

    // test boundary conditions for padding
    // 55 a's
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     55,
     {0x9f, 0x43, 0x90, 0xf8, 0xd3, 0x0c, 0x2d, 0xd9, 0x2e, 0xc9, 0xf0,
      0x95, 0xb6, 0x5e, 0x2b, 0x9a, 0xe9, 0xb0, 0xa9, 0x25, 0xa5, 0x25,
      0x8e, 0x24, 0x1c, 0x9f, 0x1e, 0x91, 0x0f, 0x73, 0x43, 0x18}},
    // 56 a's
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     56,
     {0xb3, 0x54, 0x39, 0xa4, 0xac, 0x6f, 0x09, 0x48, 0xb6, 0xd6, 0xf9,
      0xe3, 0xc6, 0xaf, 0x0f, 0x5f, 0x59, 0x0c, 0xe2, 0x0f, 0x1b, 0xde,
      0x70, 0x90, 0xef, 0x79, 0x70, 0x68, 0x6e, 0xc6, 0x73, 0x8a}},
    // 57 a's
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     57,
     {0xf1, 0x3b, 0x2d, 0x72, 0x46, 0x59, 0xeb, 0x3b, 0xf4, 0x7f, 0x2d,
      0xd6, 0xaf, 0x1a, 0xcc, 0xc8, 0x7b, 0x81, 0xf0, 0x9f, 0x59, 0xf2,
      0xb7, 0x5e, 0x5c, 0x0b, 0xed, 0x65, 0x89, 0xdf, 0xe8, 0xc6}},
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     63,
     {0x7d, 0x3e, 0x74, 0xa0, 0x5d, 0x7d, 0xb1, 0x5b, 0xce, 0x4a, 0xd9,
      0xec, 0x06, 0x58, 0xea, 0x98, 0xe3, 0xf0, 0x6e, 0xee, 0xcf, 0x16,
      0xb4, 0xc6, 0xff, 0xf2, 0xda, 0x45, 0x7d, 0xdc, 0x2f, 0x34}},
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     64,
     {0xff, 0xe0, 0x54, 0xfe, 0x7a, 0xe0, 0xcb, 0x6d, 0xc6, 0x5c, 0x3a,
      0xf9, 0xb6, 0x1d, 0x52, 0x09, 0xf4, 0x39, 0x85, 0x1d, 0xb4, 0x3d,
      0x0b, 0xa5, 0x99, 0x73, 0x37, 0xdf, 0x15, 0x46, 0x68, 0xeb}},
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     65,
     {0x63, 0x53, 0x61, 0xc4, 0x8b, 0xb9, 0xea, 0xb1, 0x41, 0x98, 0xe7,
      0x6e, 0xa8, 0xab, 0x7f, 0x1a, 0x41, 0x68, 0x5d, 0x6a, 0xd6, 0x2a,
      0xa9, 0x14, 0x6d, 0x30, 0x1d, 0x4f, 0x17, 0xeb, 0x0a, 0xe0}},
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
     66,
     {0xac, 0x13, 0x7f, 0xce, 0x49, 0x83, 0x7c, 0x7c, 0x29, 0x45, 0xf6,
      0x16, 0x0d, 0x3c, 0x0e, 0x67, 0x9e, 0x6f, 0x40, 0x07, 0x08, 0x50,
      0x42, 0x0a, 0x22, 0xbc, 0x10, 0xe0, 0x69, 0x2c, 0xbd, 0xc7}},
    // 2 block boundary conditions
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbb"
     "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
     118,
     {0x16, 0x1f, 0x88, 0x3d, 0xfb, 0x71, 0x6c, 0x8a, 0xcf, 0x49, 0x6c,
      0x04, 0x78, 0x8d, 0x42, 0xf8, 0x0c, 0xe2, 0x21, 0x50, 0x4a, 0xfa,
      0x81, 0x63, 0xc0, 0xe7, 0x3b, 0x11, 0xb5, 0xd2, 0x5f, 0xd0}},
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbb"
     "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc",
     119,
     {0x6c, 0x17, 0x1b, 0xd6, 0x6a, 0x89, 0x03, 0x44, 0xee, 0x32, 0xde,
      0x77, 0xe9, 0xdb, 0x13, 0xc0, 0x18, 0x23, 0xac, 0x16, 0xc6, 0xf4,
      0x76, 0x67, 0x69, 0xd8, 0xa3, 0xf4, 0x91, 0x27, 0xda, 0x98}},
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbb"
     "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccc",
     126,
     {0x8c, 0xfb, 0x31, 0xd1, 0x1d, 0xd7, 0x6e, 0xfb, 0x8c, 0x70, 0x38,
      0xa5, 0x09, 0xb9, 0x80, 0x69, 0xbb, 0xb7, 0xa6, 0x04, 0x67, 0x55,
      0xc5, 0x5b, 0x10, 0x83, 0x9e, 0xd8, 0x72, 0x39, 0x9b, 0x9c}},
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbb"
     "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccd",
     127,
     {0xf4, 0xaf, 0x59, 0x2b, 0xd9, 0x85, 0xc2, 0x48, 0x66, 0x64, 0x61,
      0x9a, 0xff, 0xd7, 0xf6, 0xe1, 0x97, 0x8d, 0x9b, 0x24, 0x01, 0x7c,
      0xe8, 0x63, 0x82, 0x83, 0x62, 0xa1, 0xcd, 0xcd, 0x33, 0xfc}},
    {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbb"
     "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccdd",
     128,
     {0xdb, 0x2c, 0x2b, 0xe4, 0xf8, 0x50, 0xcf, 0xd4, 0x2d, 0xce, 0x67,
      0x8b, 0xe2, 0x63, 0xa9, 0xbb, 0x74, 0x26, 0xf6, 0xbf, 0x39, 0x12,
      0x8e, 0x9e, 0x75, 0xd3, 0xa3, 0xbd, 0x02, 0x31, 0x64, 0x9a}},
    {"\x00", 1, {0x6e, 0x34, 0x0b, 0x9c, 0xff, 0xb3, 0x7a, 0x98,
                 0x9c, 0xa5, 0x44, 0xe6, 0xbb, 0x78, 0x0a, 0x2c,
                 0x78, 0x90, 0x1d, 0x3f, 0xb3, 0x37, 0x38, 0x76,
                 0x85, 0x11, 0xa3, 0x06, 0x17, 0xaf, 0xa0, 0x1d}},
    {"\x00\x01", 2, {0xb4, 0x13, 0xf4, 0x7d, 0x13, 0xee, 0x2f, 0xe6,
                     0xc8, 0x45, 0xb2, 0xee, 0x14, 0x1a, 0xf8, 0x1d,
                     0xe8, 0x58, 0xdf, 0x4e, 0xc5, 0x49, 0xa5, 0x8b,
                     0x79, 0x70, 0xbb, 0x96, 0x64, 0x5b, 0xc8, 0xd2}},
    {"\x00\x01\x02", 3, {0xae, 0x4b, 0x32, 0x80, 0xe5, 0x6e, 0x2f, 0xaf,
                         0x83, 0xf4, 0x14, 0xa6, 0xe3, 0xda, 0xbe, 0x9d,
                         0x5f, 0xbe, 0x18, 0x97, 0x65, 0x44, 0xc0, 0x5f,
                         0xed, 0x12, 0x1a, 0xcc, 0xb8, 0x5b, 0x53, 0xfc}},
    {"\x00\x01\x02\x03", 4, {0x05, 0x4e, 0xde, 0xc1, 0xd0, 0x21, 0x1f, 0x62,
                             0x4f, 0xed, 0x0c, 0xbc, 0xa9, 0xd4, 0xf9, 0x40,
                             0x0b, 0x0e, 0x49, 0x1c, 0x43, 0x74, 0x2a, 0xf2,
                             0xc5, 0xb0, 0xab, 0xeb, 0xf0, 0xc9, 0x90, 0xd8}},
};

// Sample input-output pairs for the block transform.
struct SHA256BlockTests {
  uint32_t input[16];
  uint32_t h[8];
  uint32_t want[8];
};

static const struct SHA256BlockTests kSha_bt_[] = {
    {
        {
            0,
            0xdeadbeef,
            0xbd5b7dde,
            0x9c093ccd,
            0x7ab6fbbc,
            0x5964baab,
            0x3812799a,
            0x16c03889,
            0xf56df778,
            0xd41bb667,
            0xb2c97556,
            0x91773445,
            0x7024f334,
            0x4ed2b223,
            0x2d807112,
            0xc2e3001,
        },
        {0, 0xabadcafe, 0x575b95fc, 0x30960fa, 0xaeb72bf8, 0x5a64f6f6,
         0x612c1f4, 0xb1c08cf2},
        {
            0x656f967b,
            0x508cb605,
            0x109902c5,
            0xbe9909c,
            0x30ed1bc6,
            0x8d3bb28c,
            0x836c99a8,
            0x30731a12,
        },
    },
    {
        {
            0x7,
            0xe,
            0x15,
            0x1c,
            0x23,
            0x2a,
            0x31,
            0x38,
            0x3f,
            0x46,
            0x4d,
            0x54,
            0x5b,
            0x62,
            0x69,
            0x70,
        },
        {0x0, 0x13, 0x26, 0x39, 0x4c, 0x5f, 0x72, 0x85},
        {
            0x95ddd507,
            0x1a7a4b1f,
            0xf5951676,
            0x105a25a3,
            0x511cee03,
            0xd0972a96,
            0xb1cb76d7,
            0xf9f46d72,
        },
    },
    {
        {
            0xf0cee5d1,
            0x615dfa6a,
            0xbda82adf,
            0xcb66fb25,
            0x30d60637,
            0xb1018af9,
            0x2c5c0e06,
            0xb0556e74,
            0xf8e2da1f,
            0xf05b699b,
            0xabbf6d16,
            0x3377e5ad,
            0x46d8cd9e,
            0xcc01d8dd,
            0x5532a535,
            0x34e928ea,
        },
        {
            0x515f007c,
            0x5bd062c2,
            0x12200854,
            0x4db127f8,
            0x216231b,
            0x1f16e9e8,
            0x1190cde7,
            0x66ef438d,
        },
        {
            0x3a3995a,
            0xb55e568e,
            0x5fb4b933,
            0x97c9e9c0,
            0xaea7d67c,
            0xaee17ae4,
            0xcfffacb8,
            0x91d6ab5e,
        },
    }};

// Output values for SHA256(a^k) for various values of k used in the
// benchmarking results.
struct sha256_bench {
  size_t len;
  uint8_t hash[32];
};

static const struct sha256_bench kSha_benchmark_[] = {
    {50, {0x16, 0x0b, 0x4e, 0x43, 0x3e, 0x38, 0x4e, 0x05, 0xe5, 0x37, 0xdc,
          0x59, 0xb4, 0x67, 0xf7, 0xcb, 0x24, 0x03, 0xf0, 0x21, 0x4d, 0xb1,
          0x5c, 0x5d, 0xb5, 0x88, 0x62, 0xa3, 0xf1, 0x15, 0x6d, 0x2e}},
    {114, {0x6c, 0xb7, 0x50, 0xb2, 0x18, 0x16, 0x06, 0x51, 0x1f, 0x4a, 0x7e,
           0x47, 0x86, 0xb5, 0x36, 0x92, 0xf5, 0x1a, 0x9c, 0xdc, 0x4d, 0x31,
           0x72, 0xfd, 0xcf, 0xb9, 0x26, 0x7a, 0xec, 0xa7, 0xe4, 0xe4}},
    {178, {0xca, 0xad, 0x4b, 0x2f, 0xbb, 0x83, 0x0f, 0xa5, 0x40, 0x02, 0xa1,
           0x67, 0x5f, 0x15, 0x99, 0xd9, 0xb0, 0x92, 0xcd, 0x39, 0x8a, 0xd6,
           0xfb, 0xb8, 0x16, 0xbf, 0xe8, 0xf9, 0x48, 0xcd, 0xd6, 0x20}},
    {242, {0x8f, 0xde, 0xd5, 0x9c, 0x42, 0x7b, 0x2e, 0x68, 0x09, 0xc8, 0x10,
           0x91, 0x5a, 0xac, 0xa9, 0x63, 0x57, 0x32, 0xa1, 0xde, 0xcc, 0x4b,
           0xa2, 0xac, 0x8e, 0x0a, 0x1d, 0x90, 0xfc, 0x10, 0x92, 0xc9}},
    {306, {0x4c, 0xef, 0xdb, 0xa1, 0xba, 0x8d, 0x39, 0x75, 0xf3, 0x2c, 0xba,
           0xc9, 0xf2, 0x27, 0xc7, 0xe6, 0xb6, 0xe6, 0x63, 0x43, 0x9e, 0x79,
           0x85, 0x61, 0x6b, 0xbb, 0x5c, 0x61, 0x82, 0x4d, 0xbb, 0x0c}},
    {370, {0x27, 0xdd, 0xc5, 0x8e, 0xcc, 0x58, 0xfd, 0x86, 0x93, 0x37, 0x48,
           0x1a, 0xf7, 0x73, 0x02, 0xd5, 0x4d, 0xd5, 0xba, 0x91, 0x84, 0x21,
           0x44, 0xf8, 0x7d, 0xeb, 0x83, 0x24, 0x09, 0x06, 0x9d, 0x0b}},
    {434, {0xd9, 0x67, 0xcc, 0x55, 0x05, 0x9d, 0x00, 0x82, 0xb6, 0xb2, 0x47,
           0xd2, 0xa7, 0xd7, 0xfb, 0x79, 0xb7, 0xdd, 0xed, 0xa9, 0x7b, 0xe5,
           0x92, 0x7d, 0xf4, 0x0c, 0xf9, 0x64, 0x2c, 0xee, 0xc6, 0xd3}},
    {498, {0x40, 0x47, 0x02, 0x9c, 0x99, 0xeb, 0x4e, 0xf6, 0x39, 0x25, 0x9b,
           0x2e, 0xe3, 0xc8, 0xde, 0xb4, 0x40, 0x90, 0x86, 0xc8, 0x1d, 0xe1,
           0xcd, 0xab, 0x00, 0x53, 0xbb, 0xcc, 0x08, 0x84, 0xe4, 0x1e}},
    {562, {0x3e, 0x47, 0xed, 0x99, 0x0d, 0x31, 0xf0, 0xfe, 0xb0, 0xa7, 0x77,
           0x1c, 0x72, 0xd1, 0x32, 0x0f, 0xad, 0x55, 0x58, 0x14, 0xf7, 0x81,
           0x95, 0x6e, 0xfc, 0x89, 0xda, 0x88, 0x4f, 0xf6, 0x3d, 0x40}},
    {626, {0xc6, 0x20, 0x91, 0x3d, 0xad, 0x86, 0x0a, 0x2a, 0x77, 0x3d, 0x9d,
           0x56, 0x0c, 0x2c, 0xed, 0x25, 0x03, 0xf6, 0xf9, 0xe2, 0xc5, 0x5c,
           0xcd, 0x65, 0x08, 0x3b, 0xf0, 0x53, 0x2d, 0x60, 0x62, 0x61}},
    {690, {0x21, 0x8d, 0xb2, 0x31, 0x17, 0xd4, 0x98, 0x63, 0x1b, 0xfc, 0x22,
           0x96, 0x49, 0x9a, 0xb2, 0x7e, 0x07, 0x2c, 0xce, 0xf4, 0xfa, 0xea,
           0xca, 0x63, 0xe5, 0xa2, 0xec, 0xcc, 0x7e, 0x22, 0xf4, 0xac}},
    {754, {0x29, 0x2c, 0x65, 0x83, 0xeb, 0xe8, 0x2f, 0xee, 0xec, 0xed, 0x13,
           0xa8, 0x6b, 0xdb, 0x83, 0x8a, 0x15, 0xaf, 0xcc, 0x5b, 0xf4, 0x7f,
           0x2a, 0xcf, 0x51, 0x51, 0x50, 0x7e, 0xae, 0x03, 0xd7, 0x7c}},
    {818, {0x5f, 0x99, 0x78, 0x83, 0x81, 0x11, 0x36, 0x98, 0x23, 0x16, 0x54,
           0xd9, 0x80, 0xfc, 0x73, 0x8d, 0x8e, 0x19, 0x11, 0xc6, 0xa3, 0x0d,
           0xe8, 0xcb, 0x33, 0xa2, 0x9e, 0xf7, 0xc1, 0xed, 0xc3, 0x19}},
    {882, {0x5f, 0x2e, 0xb1, 0xec, 0x0f, 0xd7, 0x93, 0x76, 0xc4, 0x8a, 0xfc,
           0x9c, 0x15, 0xd1, 0x98, 0x64, 0x94, 0x20, 0x5f, 0x69, 0x08, 0x36,
           0x43, 0xb9, 0xd6, 0x9a, 0x05, 0x85, 0xf4, 0xfb, 0xdc, 0x5f}},
    {946, {0xe8, 0x06, 0x78, 0xdf, 0xad, 0xb0, 0x70, 0x4e, 0x5e, 0x68, 0x4a,
           0xda, 0x0b, 0x0b, 0xe7, 0xac, 0x2f, 0x22, 0x54, 0x7f, 0x13, 0x04,
           0xfe, 0x9a, 0xb4, 0xaa, 0xa7, 0x30, 0xd9, 0x90, 0x9a, 0x3a}},
    {1010, {0x69, 0x4d, 0x24, 0xc6, 0x82, 0x83, 0xb9, 0x9a, 0xd5, 0xee, 0x5c,
            0xea, 0xa3, 0x5d, 0x9d, 0xe4, 0xdd, 0x55, 0x45, 0xdd, 0x7a, 0x38,
            0x5f, 0x4b, 0x18, 0xfa, 0x85, 0xb2, 0xdc, 0x6d, 0xe2, 0xba}},
    {1074, {0x55, 0xd2, 0xfe, 0x88, 0x80, 0x0f, 0x6b, 0xcb, 0xe7, 0xce, 0xa2,
            0x98, 0xf4, 0x88, 0xa7, 0xc8, 0x41, 0xd4, 0x03, 0x9f, 0x3f, 0xf3,
            0xf0, 0xc6, 0xea, 0x2f, 0xa5, 0x40, 0x84, 0xf4, 0xa0, 0xdf}},
    {1138, {0xa3, 0x38, 0x31, 0x67, 0x79, 0x4a, 0x75, 0x6d, 0x88, 0x9a, 0x7a,
            0x99, 0xf4, 0x15, 0x79, 0x4d, 0xe9, 0xdf, 0xeb, 0x21, 0x67, 0x2c,
            0xd1, 0x04, 0x41, 0xcf, 0xfd, 0x93, 0xdb, 0xd8, 0x20, 0x5a}},
    {1202, {0xdb, 0xb3, 0x3f, 0x50, 0xf1, 0xc7, 0xb1, 0xd9, 0x28, 0x63, 0x2f,
            0x81, 0x35, 0x08, 0x6a, 0x58, 0xc2, 0x6a, 0x66, 0x55, 0x3b, 0xd2,
            0x27, 0x7e, 0x4f, 0x9a, 0x5f, 0x00, 0x61, 0xc6, 0xcc, 0x25}},
    {1266, {0xeb, 0xdd, 0x77, 0xc4, 0x04, 0x96, 0xa9, 0xeb, 0x3f, 0xf9, 0x24,
            0xf4, 0xcf, 0xb5, 0x4e, 0xee, 0xb2, 0xc6, 0xf5, 0x04, 0x26, 0xb1,
            0xca, 0x2f, 0xd4, 0xd9, 0x72, 0x83, 0xd8, 0x49, 0x72, 0xce}},
    {1330, {0x05, 0x3b, 0xbf, 0x41, 0xf3, 0x3d, 0xad, 0x63, 0x5a, 0x69, 0x03,
            0xca, 0x32, 0x8f, 0x2e, 0xfd, 0x98, 0x87, 0x70, 0x2a, 0x3e, 0x02,
            0x1e, 0x6a, 0xac, 0x55, 0x9c, 0x53, 0x53, 0xec, 0x43, 0x41}},
    {1394, {0xf2, 0xda, 0xba, 0x28, 0x4a, 0x48, 0xf7, 0xc6, 0x8f, 0x72, 0x9a,
            0x17, 0x2b, 0x26, 0x3a, 0xaf, 0x97, 0xd8, 0x0c, 0x57, 0xab, 0xba,
            0x9b, 0xec, 0xa6, 0x75, 0xb9, 0x4c, 0xcb, 0x6e, 0x9d, 0xa7}},
    {1458, {0x20, 0x2f, 0x4f, 0x63, 0xe9, 0x31, 0x90, 0x1a, 0xe1, 0xcb, 0x8d,
            0x42, 0x1d, 0x16, 0x9b, 0x12, 0x78, 0x2a, 0x58, 0x51, 0xca, 0x49,
            0x73, 0x72, 0xf4, 0xf4, 0x7c, 0x6f, 0xea, 0xc7, 0x61, 0x1d}},
    {1522, {0xee, 0x97, 0x06, 0xa8, 0xbd, 0x8b, 0x8f, 0xbe, 0x96, 0xa9, 0xff,
            0x52, 0xb7, 0xc6, 0x36, 0x02, 0x80, 0x70, 0x5f, 0x74, 0xbe, 0x73,
            0xb8, 0x65, 0x5b, 0xdb, 0x8d, 0x56, 0x07, 0x61, 0xa7, 0xad}},
    {1586, {0x60, 0xd6, 0x6a, 0xa2, 0x83, 0xbf, 0x1c, 0xed, 0x60, 0x6f, 0xa6,
            0x40, 0x73, 0xf4, 0x36, 0x3d, 0xed, 0x46, 0x92, 0x4e, 0x93, 0xbe,
            0x67, 0xb8, 0x19, 0x0b, 0x04, 0xa5, 0x70, 0xab, 0x4a, 0xb5}},
    {1650, {0x93, 0x32, 0xff, 0xe0, 0x58, 0x84, 0xbf, 0x8f, 0xdd, 0x09, 0x2f,
            0x94, 0x43, 0x22, 0x57, 0x28, 0x4f, 0x9c, 0x23, 0x2a, 0x72, 0xe5,
            0xaa, 0x3b, 0x9b, 0x11, 0xda, 0xc4, 0x57, 0xe3, 0x46, 0x34}},
    {1714, {0xd3, 0x47, 0xa1, 0xbe, 0xf9, 0xea, 0x05, 0x0d, 0xe5, 0x06, 0x2e,
            0x27, 0x86, 0x72, 0x7d, 0x2b, 0x4c, 0xcb, 0x60, 0x77, 0x7c, 0x82,
            0xf9, 0x7c, 0xd7, 0x58, 0xfa, 0x44, 0x27, 0x35, 0x51, 0x42}},
    {1778, {0xdd, 0xf6, 0x34, 0x5e, 0x0e, 0xd2, 0x19, 0x75, 0x06, 0xac, 0x22,
            0x9f, 0x3b, 0x5f, 0x53, 0xe8, 0xc0, 0x44, 0x24, 0x1d, 0xf6, 0x52,
            0x72, 0x79, 0xdd, 0x08, 0xa1, 0x2c, 0xbf, 0x75, 0x9f, 0x5e}},
    {1842, {0xd3, 0xc1, 0x90, 0xe9, 0x4b, 0x65, 0xd4, 0xd6, 0x87, 0xf4, 0xa0,
            0x14, 0xc6, 0x9a, 0x21, 0x93, 0x87, 0x64, 0x19, 0xcc, 0xa9, 0xa4,
            0x11, 0xf5, 0xdb, 0x10, 0x03, 0x29, 0xb2, 0x44, 0xe7, 0x98}},
    {1906, {0x5a, 0x13, 0x1d, 0xa8, 0x27, 0xb8, 0x8a, 0x0e, 0x43, 0x40, 0x7b,
            0x2b, 0x61, 0x5a, 0x9b, 0x39, 0x1f, 0x80, 0xee, 0x45, 0xab, 0x70,
            0x55, 0x6c, 0x13, 0xa4, 0x72, 0xe2, 0xa6, 0xf5, 0x79, 0xb3}},
    {1970, {0x36, 0xaf, 0x67, 0x22, 0xf2, 0xf8, 0x6f, 0x99, 0x3d, 0x5f, 0x36,
            0x02, 0xc3, 0x1f, 0x3d, 0xea, 0xbd, 0x34, 0xdc, 0xf1, 0xe4, 0xe4,
            0x78, 0x73, 0x05, 0x20, 0xea, 0xec, 0xf8, 0x71, 0x76, 0xac}},
    {2034, {0xad, 0x21, 0x5f, 0x13, 0xa8, 0xf7, 0x7d, 0xa1, 0xd0, 0x15, 0xc3,
            0x5a, 0xdd, 0xee, 0xf8, 0x08, 0x54, 0x79, 0x51, 0xa0, 0x1b, 0x06,
            0x58, 0x0d, 0x06, 0x31, 0x29, 0x3a, 0xab, 0x6b, 0x24, 0x31}},
    {2098, {0x5b, 0xff, 0x21, 0x09, 0x0b, 0x8c, 0x80, 0xcf, 0xaf, 0x9c, 0xae,
            0x93, 0x11, 0x75, 0x05, 0x3e, 0xb2, 0x37, 0x9b, 0xa4, 0xe5, 0xd5,
            0xa1, 0x2a, 0x60, 0x7c, 0x55, 0x57, 0x72, 0x10, 0xf0, 0xb3}},
    {2162, {0xad, 0x56, 0x0c, 0xd1, 0x7d, 0x76, 0x14, 0x5f, 0x19, 0x34, 0x14,
            0x78, 0x31, 0x88, 0x89, 0xdc, 0x35, 0x58, 0xb1, 0x83, 0xed, 0xd4,
            0x6a, 0x80, 0x07, 0x82, 0x93, 0xdf, 0x94, 0xd9, 0x35, 0x43}},
    {2226, {0x49, 0x2d, 0x5a, 0x85, 0xfc, 0x33, 0x49, 0xc1, 0x01, 0x83, 0xb0,
            0x54, 0x80, 0x3c, 0x6d, 0x97, 0x52, 0x8d, 0xcf, 0x61, 0x5b, 0x7a,
            0x6f, 0xfa, 0x12, 0x97, 0xee, 0x46, 0x3b, 0x48, 0x0b, 0x08}},
    {2290, {0x9b, 0xfc, 0x47, 0x6b, 0xc9, 0x7e, 0x17, 0x78, 0xd9, 0x20, 0x9f,
            0x8e, 0x1f, 0x5b, 0xa5, 0x79, 0x1d, 0xad, 0x62, 0xb0, 0x9d, 0xf2,
            0x45, 0x33, 0x17, 0x4e, 0xf6, 0x8f, 0x3a, 0x04, 0xfb, 0x11}},
    {2354, {0x7a, 0xbb, 0x72, 0xe9, 0x1a, 0xb0, 0x42, 0x7d, 0xf0, 0x33, 0xaa,
            0xb7, 0x21, 0xaa, 0x8c, 0x0a, 0xd0, 0x73, 0xe7, 0xd0, 0xea, 0x03,
            0x40, 0x15, 0xda, 0x17, 0x9c, 0xe7, 0x85, 0xf1, 0x0e, 0x1e}},
    {2418, {0x21, 0xc6, 0xcc, 0xdb, 0x95, 0x84, 0x53, 0x12, 0xe3, 0xc9, 0x35,
            0xff, 0xb8, 0xff, 0x69, 0xc2, 0x6e, 0x00, 0x4e, 0x9c, 0x10, 0x80,
            0xb0, 0x0d, 0x82, 0xfb, 0x17, 0x77, 0x98, 0x23, 0x52, 0x66}},
    {2482, {0xda, 0x0f, 0x57, 0x79, 0x4d, 0x99, 0x1e, 0x88, 0xd7, 0x4e, 0x03,
            0x86, 0x63, 0x2a, 0xeb, 0x24, 0xc8, 0x60, 0xe8, 0xc7, 0xff, 0x62,
            0x5d, 0x5a, 0xd1, 0x20, 0x03, 0x0c, 0x78, 0xb5, 0xba, 0x62}}};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_SHA256_TEST_VALUES_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_UTILITY_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_UTILITY_H_

#include <stddef.h>

#include <cstdint>

namespace proofs {
template <class Field>
class AlgebraUtil {
 public:
  using Elt = typename Field::Elt;

  // a[i*da] = inverse(b[i*db]), via Montgomery batch inversion
  static void batch_invert(size_t n, Elt a[/*n with stride da*/], size_t da,
                           const Elt b[/*n with stride db*/], size_t db,
                           const Field& F) {
    Elt p = F.one();

    // a[i] \gets \prod_{j<i] b[j]
    for (size_t i = 0; i < n; ++i) {
      Elt bi = b[i * db];
      a[i * da] = p;
      F.mul(p, bi);
    }

    // now p = \prod_{j<n] b[j]
    F.invert(p);

    for (size_t i = n; i-- > 0;) {
      F.mul(a[i * da], p);
      F.mul(p, b[i * db]);
    }
  }

  // a[i] = 1/i, with a[0]=0
  static void batch_inverse_arithmetic(size_t n, Elt a[/*n*/], const Field& F) {
    a[0] = F.zero();
    // this is essentially batch_inverse with b[i]=bi

    Elt p = F.one();
    Elt bi = F.zero();

    for (size_t i = 1; i < n; ++i) {
      F.add(bi, F.one());
      a[i] = p;
      F.mul(p, bi);
    }

    // now p = \prod_{j<n] b[j]
    F.invert(p);

    for (size_t i = n; i-- > 0;) {
      F.mul(a[i], p);
      F.mul(p, bi);
      F.sub(bi, F.one());
    }
  }

  static Elt factorial(uint64_t n, const Field& F) {
    auto p = F.one();
    auto fi = F.one();
    for (uint64_t i = 1; i <= n; ++i) {
      F.mul(p, fi);
      F.add(fi, F.one());
    }
    return p;
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_UTILITY_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_GF2K_GF2POLY_H_
#define PRIVACY_PROOFS_ZK_LIB_GF2K_GF2POLY_H_

#include <array>
#include <cstddef>
#include <cstdint>

#include "algebra/limb.h"

namespace proofs {

// Rough equivalent of Nat<W64> but representing polynomials
// over GF2 instead of natural numbers.
template <size_t W64>
class GF2Poly : public Limb<W64> {
 public:
  using Super = Limb<W64>;
  using T = GF2Poly<W64>;
  using Super::kLimbs;
  using Super::kU64;
  using Super::limb_;

  GF2Poly() = default;  // uninitialized
  explicit GF2Poly(uint64_t x) : Super(x) {}

  explicit GF2Poly(const std::array<uint64_t, kU64>& a) : Super(a) {}

  bool operator<(const T& other) const {
    for (size_t i = kLimbs; i-- > 0;) {
      if (limb_[i] < other.limb_[i]) {
        return true;
      }
      if (limb_[i] > other.limb_[i]) {
        return false;
      }
    }
    return false;
  }

  // Interpret A[] as a little-endian nat
  static T of_bytes(const uint8_t a[/* kBytes */]) {
    T r;
    for (size_t i = 0; i < kLimbs; ++i) {
      a = Super::of_bytes(&r.limb_[i], a);
    }
    return r;
  }

  T& add(const T& y) {
    for (size_t i = 0; i < kLimbs; ++i) {
      limb_[i] ^= y.limb_[i];
    }
    return *this;
  }
  T& sub(const T& y) { return add(y); }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_GF2K_GF2POLY_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP_H_

#include <cstddef>

#include "algebra/fp_generic.h"
#include "algebra/sysdep.h"

namespace proofs {

/*
The FpReduce structure factors out the main routine for performing modular
reduction wrt to a Montgomery-represented field element in the FpGeneric
class. This struct contains a generic reduction step that always works,
but it can be specialized for certain primes to achieve better efficiency as
done with our 128- and 256- bit fields.
*/
struct FpReduce {
  template <class limb_t, class N>
  static inline void reduction_step(limb_t a[], limb_t mprime, const N& m) {
    constexpr size_t kLimbs = N::kLimbs;
    if (kLimbs == 1) {
      // The general case (below) represents the (kLimbs+1)-word product as
      // L+(H<<64), where in general L and H overlap, requiring
      // two additions.  For kLimbs==1, L and H do not overlap, and we can
      // interpret [L, H] as a single double-precision number.
      limb_t lh[2];
      limb_t r = mprime * a[0];
      mulhl(1, lh, lh + 1, r, m.limb_);
      accum(3, a, 2, lh);
    } else {
      limb_t l[kLimbs], h[kLimbs];
      limb_t r = mprime * a[0];
      mulhl(kLimbs, l, h, r, m.limb_);
      accum(kLimbs + 2, a, kLimbs, l);
      accum(kLimbs + 1, a + 1, kLimbs, h);
    }
  }
};

template <size_t W, bool optimized_mul = false>
using Fp = FpGeneric<W, optimized_mul, FpReduce>;
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_SUMCHECK_VERIFIER_H_
#define PRIVACY_PROOFS_ZK_LIB_SUMCHECK_VERIFIER_H_

#include <stddef.h>

#include <memory>

#include "arrays/dense.h"
#include "random/transcript.h"
#include "sumcheck/circuit.h"
#include "sumcheck/transcript_sumcheck.h"
#include "sumcheck/verifier_layers.h"

namespace proofs {
// Full sumcheck verifier that verifies the layers
// via verifier_layers<> and then checks the input
// binding directly.
template <class Field>
class Verifier : public VerifierLayers<Field> {
  using super = VerifierLayers<Field>;
  using typename super::claims;
  using typename super::Elt;

 public:
  static bool verify(const char** why, const Circuit<Field>* circ,
                      const Proof<Field>* proof,
                      std::unique_ptr<Dense<Field>> V,
                      std::unique_ptr<Dense<Field>> X, Transcript& ts,
                      const Field& F) {
    if (why == nullptr || circ == nullptr || proof == nullptr ||
        V == nullptr || X == nullptr) {
      return false;
    }

    claims cl{};
    Challenge<Field> ch(circ->nl);
    TranscriptSumcheck<Field> tss(ts, F);
    tss.write_input(X.get());

    if (!(super::circuit(why, &cl, circ, proof, &ch, std::move(V), tss,
                         F))) {
      return false;
    }

    // Final check on W, the input wires.
    // bind the copy variables:
    X->bind_all(circ->logc, cl.q, F);
    X->reshape(cl.nv);

    // bind the gate variables, for two hands:
    auto X1 = X->clone();
    Dense<Field>* VH[2] = {X.get(), X1.get()};

    for (size_t hand = 0; hand < 2; ++hand) {
      VH[hand]->bind_all(cl.logv, cl.g[hand], F);
      Elt got = VH[hand]->scalar();
      if (got != cl.claim[hand]) {
        *why = "got != cl.claim[hand]";
        return false;
      }
    }

    return true;
  }

  Verifier() = delete;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_SUMCHECK_VERIFIER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ZK_ZK_TESTING_H_
#define PRIVACY_PROOFS_ZK_LIB_ZK_ZK_TESTING_H_

#include <cstddef>
#include <cstdint>
#include <vector>

#include "algebra/convolution.h"
#include "algebra/fp2.h"
#include "algebra/reed_solomon.h"
#include "arrays/dense.h"
#include "random/secure_random_engine.h"
#include "random/transcript.h"
#include "sumcheck/circuit.h"
#include "util/log.h"
#include "util/readbuffer.h"
#include "zk/zk_proof.h"
#include "zk/zk_prover.h"
#include "zk/zk_verifier.h"
#include "gtest/gtest.h"

namespace proofs {

constexpr size_t kLigeroRate = 4;
constexpr size_t kLigeroNreq = 189;

// Runs a zk prover and verifier for a field that requires a field extension
// to perform the commitment.
template <class Field>
void run2_test_zk(const Circuit<Field>& circuit, Dense<Field>& W,
                  const Dense<Field>& pub, const Field& base,
                  const typename Field::Elt& root_x,
                  const typename Field::Elt& root_y, size_t root_order) {
  // Build the relevant algebra objects.
  using Field2 = Fp2<Field>;
  using Elt2 = typename Field2::Elt;
  using FftExtConvolutionFactory = FFTExtConvolutionFactory<Field, Field2>;
  using RSFactory = ReedSolomonFactory<Field, FftExtConvolutionFactory>;

  const Field2 base_2(base);
  const Elt2 omega{root_x, root_y};
  const FftExtConvolutionFactory fft(base, base_2, omega, root_order);
  const RSFactory rsf(fft, base);

  ZkProof<Field> zkpr(circuit, kLigeroRate, kLigeroNreq);

  Transcript tp((uint8_t*)"zk_test", 7);
  SecureRandomEngine rng;
  ZkProver<Field, RSFactory> prover(circuit, base, rsf);
  prover.commit(zkpr, W, tp, rng);
  EXPECT_TRUE(prover.prove(zkpr, W, tp));
  log(INFO, "ZK Prover done");

  std::vector<uint8_t> zbuf;
  zkpr.write(zbuf, base);
  log(INFO, "zkp len: %zu bytes", zbuf.size());

  // ======= run verifier =============
  // Re-parse the proof to simulate a different client.
  ZkProof<Field> zkpv(circuit, kLigeroRate, kLigeroNreq);
  ReadBuffer rb(zbuf);
  EXPECT_TRUE(zkpv.read(rb, base));

  ZkVerifier<Field, RSFactory> verifier(circuit, rsf, kLigeroRate, kLigeroNreq,
                                        base);
  Transcript tv((uint8_t*)"zk_test", 7);
  verifier.recv_commitment(zkpv, tv);
  EXPECT_TRUE(verifier.verify(zkpv, pub, tv));
  log(INFO, "ZK Verify done");
}

template <class Field>
void run_failing_test_zk2(const Circuit<Field>& circuit, Dense<Field>& W,
                          const Dense<Field>& pub, const Field& base,
                          const typename Field::Elt& root_x,
                          const typename Field::Elt& root_y,
                          size_t root_order) {
  // Build the relevant algebra objects.
  using Field2 = Fp2<Field>;
  using Elt2 = typename Field2::Elt;
  using FftExtConvolutionFactory = FFTExtConvolutionFactory<Field, Field2>;
  using RSFactory = ReedSolomonFactory<Field, FftExtConvolutionFactory>;

  const Field2 base_2(base);
  const Elt2 omega{root_x, root_y};
  const FftExtConvolutionFactory fft(base, base_2, omega, root_order);
  const RSFactory rsf(fft, base);

  ZkProof<Field> zkpr(circuit, kLigeroRate, kLigeroNreq);

  Transcript tp((uint8_t*)"zk_test", 7);
  SecureRandomEngine rng;
  ZkProver<Field, RSFactory> prover(circuit, base, rsf);
  prover.commit(zkpr, W, tp, rng);
  bool p_ok = prover.prove(zkpr, W, tp);
  EXPECT_FALSE(p_ok);
}

// Runs a zk prover and verifier for a field that has a suitable root of unity.
template <class Field>
void run_test_zk(const Circuit<Field>& circuit, Dense<Field>& W,
                 const Dense<Field>& pub, const typename Field::Elt& omega,
                 uint64_t omega_order, const Field& F) {
  using FftConvolutionFactory = FFTConvolutionFactory<Field>;

  FftConvolutionFactory fft(F, omega, omega_order);
  using RSFactory = ReedSolomonFactory<Field, FftConvolutionFactory>;
  const RSFactory rsf(fft, F);

  ZkProof<Field> zkpr(circuit, kLigeroRate, kLigeroNreq);

  Transcript tp((uint8_t*)"zk_test", 7);
  SecureRandomEngine rng;
  ZkProver<Field, RSFactory> prover(circuit, F, rsf);
  prover.commit(zkpr, W, tp, rng);
  EXPECT_TRUE(prover.prove(zkpr, W, tp));

  log(INFO, "ZK Prover done");

  std::vector<uint8_t> zbuf;
  zkpr.write(zbuf, F);
  log(INFO, "zkp len: %zu bytes", zbuf.size());

  // ======= zk verifier =============
  // Re-parse the proof to simulate a different client.
  ZkProof<Field> zkpv(circuit, kLigeroRate, kLigeroNreq);
  ReadBuffer rb(zbuf);
  EXPECT_TRUE(zkpv.read(rb, F));

  ZkVerifier<Field, RSFactory> verifier(circuit, rsf, kLigeroRate, kLigeroNreq,
                                        F);
  Transcript tv((uint8_t*)"zk_test", 7);
  verifier.recv_commitment(zkpv, tv);
  EXPECT_TRUE(verifier.verify(zkpv, pub, tv));
}

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ZK_ZK_TESTING_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_UTIL_CEILDIV_H_
#define PRIVACY_PROOFS_ZK_LIB_UTIL_CEILDIV_H_

// This package holds basic math utility functions.

#include <cstddef>
#include <cstdint>

namespace proofs {

// ceil(a/b)
template <class T>
T ceildiv(T a, T b) {
  return (a + (b - 1)) / b;
}

inline size_t lg(size_t n) {
  size_t lgk = 0, k = 1;
  while (k < n) {
    k *= 2;
    lgk += 1;
  }
  return lgk;
}

// Morton-order operations
namespace morton {
// extract even bits (pack)
inline uint64_t even(uint64_t x) {
  x &= 0x5555555555555555ull;
  x |= (x >> 1);
  x &= 0x3333333333333333ull;
  x |= (x >> 2);
  x &= 0x0F0F0F0F0F0F0F0Full;
  x |= (x >> 4);
  x &= 0x00FF00FF00FF00FFull;
  x |= (x >> 8);
  x &= 0x0000FFFF0000FFFFull;
  x |= (x >> 16);
  x &= 0x00000000FFFFFFFFull;
  return x;
}

// inverse of even (unpack)
inline uint64_t uneven(uint64_t x) {
  x &= 0x00000000FFFFFFFFull;
  x |= (x << 16);
  x &= 0x0000FFFF0000FFFFull;
  x |= (x << 8);
  x &= 0x00FF00FF00FF00FFull;
  x |= (x << 4);
  x &= 0x0F0F0F0F0F0F0F0Full;
  x |= (x << 2);
  x &= 0x3333333333333333ull;
  x |= (x << 1);
  x &= 0x5555555555555555ull;
  return x;
}

// Given two integers X and Y represented
// as (even, odd) bits (X0, X1) and
// (Y0, Y1), set (X0, X1) to the even/odd
// representation of X+Y
template <class T>
static void add(T *x0, T *x1, T y0, T y1) {
  // Given two arrays X[i] and Y[i] of bits, the goal
  // is to build an adder.  One way to build an adder
  // is to switch to the generate/propagate representation
  //   G[i] = X[i] & Y[i]
  //   P[i] = X[i] ^ Y[i]
  // where G[i] means "position i generates a carry" and P[i] means
  // "position i propagates the carry coming from position i-1".
  //
  // Generate/propagate can be extended to pairs of positions
  // via the equations
  //
  //   G = G[i+1] ^ (G[i] ^ P[i+1])
  //   P = P[i+1] & P[i].                       (1)
  //
  // (This is all well-known adder stuff that has been known since
  // at least the '50s).
  //
  // Our strategy is thus: convert the addends into G/P representation;
  // combine the [2i] and [2i+1] positions via Equation (1), and
  // use the C "+" operation to propagate the carry over one array.
  //
  // The fun part is, how do you use the C adder to propagate G.
  // The standard form of the adder is:
  //
  //   (G, P) = (X & Y, X ^ Y)
  //   G' = propagate G in any convenient way
  //   (X + Y) = RESULT = P ^ G'
  //
  // and thus we can extract the propagated G' as G' = (X + Y) ^ X ^ Y.
  //
  // The other fun part is, given G and P, how do you go back to X and
  // Y that can be fed to the C adder?  The transformation (X, Y) -> (G, P)
  // is not injective, but any inverse will work.  We choose
  //
  //   X = G
  //   Y = P ^ G

  // Convert inputs into (G, P) form.
  T g0 = *x0 & y0, g1 = *x1 & y1;
  T p0 = *x0 ^ y0, p1 = *x1 ^ y1;

  // Combine the two (G, P) inputs.
  T g = g1 ^ (g0 & p1);
  T p = p0 & p1;

  // Convert back into (X, Y) = (G, P ^ G) and compute
  // GPRIME = (X + Y) ^ X ^ Y, which simplifies to (X + Y) ^ P
  // because X = G and Y = P ^ G.
  // Here we lose the carry of the addition, making it impossible
  // to output a global carry.
  T gprime = (g + (p ^ g)) ^ p;

  // XOR the propagated carries back into P
  *x0 = gprime ^ p0;
  *x1 = g0 ^ (gprime & p0) ^ p1;
}

// a-b via ~(~a + b)
template <class T>
static void sub(T *x0, T *x1, T y0, T y1) {
  *x0 = ~*x0;
  *x1 = ~*x1;
  add(x0, x1, y0, y1);
  *x0 = ~*x0;
  *x1 = ~*x1;
}

// a < b via (a - b) < 0.  Since we don't have
// the output carry of the subtraction, we pretend that
// the result is signed.
template <class T>
static bool lt(T x0, T x1, T y0, T y1) {
  sub(&x0, &x1, y0, y1);
  return (x1 >> (8 * sizeof(T) - 1)) == 1;
}

template <class T>
static bool eq(T x0, T x1, T y0, T y1) {
  return x0 == y0 && x1 == y1;
}

}  // namespace morton
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_UTIL_CEILDIV_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ARRAYS_EQ_H_
#define PRIVACY_PROOFS_ZK_LIB_ARRAYS_EQ_H_

#include <stddef.h>

#include "arrays/affine.h"

namespace proofs {
template <class Field>
// EQ[i,j] is 2D sparse array EQ[i, j] = (i == j).
// This class contains a state-free version of EQ, which
// evaluates EQ[i, j] on the fly.  See Eqs for a stateful
// version that stores all the values of EQ[I, j] for fixed I
// and variable j.
class Eq {
  using Elt = typename Field::Elt;

 public:
  /*
    Bind EQ{logn,n} at I, J.

    We consider the diagonal matrix EQ[i,j] to be composed of
    N-1 diagonal elements A and one last diagonal element B, i.e.,
    EQ=diag([A A A A ... B]).  We bind one I variable and one J
    variable in one step, yielding a matrix of the same form
    with ceil(n/2) diagonal entries.

    Let I1J1=I[0]*J[0] and I0J0=(1-I[0])*(1-J[0]).

    Binding A is equivalent to binding the 2x2 block [A 0; 0 A],
    yielding A <- A*(I0J0+I1J1).

    If n is even, then the last 2x2 block is [A 0; 0 B], whose binding
    yields B <- A*I0J0 + B*I1J1.

    If n is odd, then the last 2x2 block is [B 0; 0 0], whose binding
    yields B <- B*I0J0.
  */
  static Elt eval(size_t logn, corner_t n, const Elt I[/*logn*/],
                  const Elt J[/*logn*/], const Field& F) {
    Elt a = F.one(), b = F.one();
    for (size_t round = 0; round < logn; round++) {
      Elt i1 = I[round], j1 = J[round];
      Elt i0 = F.subf(F.one(), i1), j0 = F.subf(F.one(), j1);
      Elt i0j0 = F.mulf(i0, j0);
      Elt i1j1 = F.mulf(i1, j1);
      if ((n & 1) == 0) {
        F.mul(b, i1j1);
        F.add(b, F.mulf(a, i0j0));
      } else {
        F.mul(b, i0j0);
      }
      F.mul(a, F.addf(i0j0, i1j1));
      n = (n + 1) / 2;
    }
    return b;
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ARRAYS_EQ_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_TWIDDLE_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_TWIDDLE_H_

#include <stddef.h>
#include <stdint.h>

#include <vector>

// Twiddle factors for FFT
namespace proofs {

template <class Field>
class Twiddle {
  using Elt = typename Field::Elt;

 public:
  size_t order_;
  // powers of omega_n
  std::vector<Elt> w_;

  explicit Twiddle(size_t n, const Elt& omega_n, const Field& F)
      : order_(n), w_(n / 2) {
    auto w = F.one();
    for (size_t i = 0; 2 * i < n; ++i) {
      w_[i] = w;
      F.mul(w, omega_n);
    }
  }

  // given a n-th root of unity omega_n, return a r-th root of unity
  // for r <= n
  static Elt reroot(const Elt& omega_n, uint64_t n, uint64_t r,
                    const Field& F) {
    Elt omega_r = omega_n;
    while (r < n) {
      F.mul(omega_r, omega_r);
      r += r;
    }
    return omega_r;
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_TWIDDLE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_BOGORNG_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_BOGORNG_H_

namespace proofs {
// Totally bogus "random" number generator, used only for testing.
// There is no guarantee that it will cycle over all elements in the
// field, but this keeps dependencies internal to this directory.
// The public and internal functions of this class all take a const Field&
// parameter to produce random elements in the Field. It is the caller's
// responsibility to ensure the object remains valid during execution.
template <class Field>
class Bogorng {
  using Elt = typename Field::Elt;

 public:
  explicit Bogorng(const Field* F)
      : f_(F), next_(F->of_scalar_field(123456789u)) {}

  Elt next() {
    // really old-school
    f_->mul(next_, f_->of_scalar_field(1103515245u));
    f_->add(next_, f_->of_scalar_field(12345u));
    return next_;
  }

  Elt nonzero() {
    Elt x;
    do {
      x = next();
    } while (x == f_->zero());
    return x;
  }

 private:
  const Field* f_;
  Elt next_;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_BOGORNG_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP_P256_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP_P256_H_

#include <array>
#include <cstdint>

#include "algebra/fp_generic.h"
#include "algebra/nat.h"
#include "algebra/sysdep.h"

namespace proofs {
// Optimized implementation of
// Fp(115792089210356248762697446949407573530086143415290314195533631308867097853951)

/*
This struct contains an optimized reduction step for the chosen field.
*/
struct Fp256Reduce {
  // Harcoded base_64 modulus.
  static const constexpr std::array<uint64_t, 4> kModulus = {
      0xFFFFFFFFFFFFFFFFu,
      0xFFFFFFFFu,
      0,
      0xFFFFFFFF00000001u,
  };

  static inline void reduction_step(uint64_t a[], uint64_t mprime,
                                    const Nat<4>& m) {
    uint64_t r = a[0];
    uint64_t l[5] = {r, 0, 0, r << 32, r >> 32};
    negaccum(6, a, 5, l);
    uint64_t h[4] = {r << 32, r >> 32, r, r};
    accum(5, a + 1, 4, h);
  }

  static inline void reduction_step(uint32_t a[], uint32_t mprime,
                                    const Nat<4>& m) {
    uint32_t r = a[0];
    uint32_t l[8] = {r, 0, 0, 0, 0, 0, 0, r};
    negaccum(10, a, 8, l);
    uint32_t h[6] = {r, 0, 0, r, 0, r};
    accum(7, a + 3, 6, h);
  }
};

template <bool optimized_mul = false>
using Fp256 = FpGeneric<4, optimized_mul, Fp256Reduce>;
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FP_P256_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_TRANSCRIPT_H_
#define PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_TRANSCRIPT_H_

#include <stddef.h>

#include <array>

#include "ligero/ligero_param.h"
#include "random/transcript.h"

namespace proofs {
template <class Field>
class LigeroTranscript {
 public:
  using Elt = typename Field::Elt;

  static void write_commitment(const LigeroCommitment<Field>& commitment,
                               Transcript& ts) {
    ts.write(commitment.root.data, commitment.root.kLength);
  }

  static void gen_uldt(Elt u[/*nwqrow*/], const LigeroParam<Field>& p,
                       Transcript& ts, const Field& F) {
    ts.elt(u, p.nwqrow, F);
  }

  static void gen_alphal(size_t nl, Elt alpha[/*nl*/], Transcript& ts,
                         const Field& F) {
    ts.elt(alpha, nl, F);
  }

  static void gen_alphaq(std::array<Elt, 3> alpha[/*nq*/],
                         const LigeroParam<Field>& p, Transcript& ts,
                         const Field& F) {
    ts.elt(&alpha[0][0], 3 * p.nq, F);
  }

  static void gen_uquad(Elt u[/*nqtriples*/], const LigeroParam<Field>& p,
                        Transcript& ts, const Field& F) {
    ts.elt(u, p.nqtriples, F);
  }

  // Choose p.nreq distinct naturals in [0, p.block_enc - p.dblock)
  static void gen_idx(size_t idx[/*p.nreq*/], const LigeroParam<Field>& p,
                      Transcript& ts, const Field& F) {
    check(p.block_enc >= p.dblock, "p.block_enc >= p.dblock");
    check(p.block_enc - p.dblock >= p.nreq, "p.block_enc - p.dblock >= p.nreq");
    ts.choose(idx, p.block_enc - p.dblock, p.nreq);
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_LIGERO_LIGERO_TRANSCRIPT_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_IO_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_IO_H_

#include <cstddef>
namespace proofs {

static constexpr size_t kDateLen = 8;

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ANONCRED_SMALL_IO_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_EXAMPLES_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_EXAMPLES_H_

#include <cstddef>
#include <cstdint>

#include "algebra/static_string.h"

namespace proofs {

static const StaticString kIssuerPKX[] = {
    StaticString(
        "0x2c80c10bf70f63bddcc41ea20d76a22ecba2a97fa8811bf19d572433b12c0c1f"),
    StaticString(
        "0xcc900843699408936c912474a27abb7b62d7c90e4f6d8557714825476eee2366"),
    StaticString(
        "0xdc1c1f55cff4cd5c76cf4169278f7217667f86ee81d8669b63f2e19bc12a0c9f"),

    // AZ MVMProdCA
    StaticString(
        "0xbace36c902b2bdfc41b4b36ae7e56b17d571424d8a04395cb2d9d42325436c5b"),
    // AZ MVMProdCA
    StaticString(
        "0x8b90d350003c890c55f8d5ec8c8d39a6eb3a7551a6fa5ca3c68cbeca32fe6666"),
    // US-AZ MVMTestCA
    StaticString(
        "0xf228c62ac2cbafff3fa728010b4a99c098e5d0ff27084155950c4e3fdabbff47"),
    // California DMV IACA Root
    StaticString(
        "0x60cca07942c457606c6621aa40de294ba17083c604cc236240ac0ef6550f602f"),
    // Colorado Root Certificate
    StaticString(
        "0xc3b5f6418a146d4f840ee9bf47d18eb1b27705abca2c423692721795fb8ff5cb"),
    // CO DRIVES Root
    StaticString(
        "0xe93b68b25e9a8c8a42b24e3595b83dbe2a2091d3e18cf0a735062abca578b4a4"),
    // Georgia Root Certificate Authority
    StaticString(
        "0xac469d749c214ab56eac24bd3130f30a0c291e28571c2dd4462a5da037db67cc"),
    // Georgia Root Certificate Authority
    StaticString(
        "0xbcc1c0025e04d985a34519e1ab3f721556aaa8ab658e591bb2bed40044782de0"),
    // Maryland MVA, CN=Fast Enterprises Root
    StaticString(
        "0x69670a22a94059ba15f774406b969dd0b2419fc5b4ff2630b4ec94971b93c68a"),
    // Maryland MVA, CN=Fast Enterprises Root
    StaticString(
        "0x530664d11e9e497f2d56ecb45ec876a543e5c153009f7b460514055883f9d0c6"),
    // New Mexico Root Certificate Authority
    StaticString(
        "0xe17985694bcec58d553e0f6d5179a53279264755c32d5d03148499b8eeeb7348"),
    // Google UL TESTING Staging DS
    StaticString(
        "0xcc900843699408936c912474a27abb7b62d7c90e4f6d8557714825476eee2366"),
    // C=US
    StaticString(
        "0xd2cfd2ab7d5b52d6ce2178bf94505d1435c87572a7874e6ad4acf18bd35c1be1"),
    // State Of Utopia Issuing Authority Signing Key
    StaticString(
        "0xac33541815b08cdd996aa325ba1a2b6f5c87a755c0c05956dca8b5dbdf73af69"),
};

static const StaticString kIssuerPKY[] = {
    StaticString(
        "0x3f994c043be7e17dd08387281bac0c37a529361b3cb36a0fac38d41ac066f903"),
    StaticString(
        "0xb19764d33b530e9bd6a87081f1d8e5cbc80db640ac962ee8a3e0393c14c6bfb4"),
    StaticString(
        "0x12355dd0385fed3bc33bedc9781b9aad47b33e4c24704b8d14288b1b3cb45c28"),

    // AZ MVMProdCA
    StaticString(
        "0xc2b612dd77e71e4a3a8642d4ef5a106b4e97618b194d2c1babdcae21f8304589"),
    // AZ MVMProdCA
    StaticString(
        "0x9a81b4198f1321f17f3caf33fac082f3dff6777185e8de2bb9cea8475b317fb7"),
    // US-AZ MVMTestCA
    StaticString(
        "0x1f79e1ecc5b9c6844bffa4c10b41cdbf4d514641a44eb6954913909db0964ed1"),
    // California DMV IACA Root
    StaticString(
        "0x60c82a4040725298b62c7bbd0c146e16c7efa9a799b3854b1625089b6295f975"),
    // Colorado Root Certificate
    StaticString(
        "0x22134720c33bbcddc1e237c24a27e535b9f33d4d60752b03be8231e8569bdc8c"),
    // CO DRIVES Root
    StaticString(
        "0x0f9ec3881c5ffbf7c360aaf4f591003f4b08aebe992cb7a8d5ccd8e77dbeacae"),
    // Georgia Root Certificate Authority
    StaticString(
        "0x68bd68479edf3f52faf387d84293c455668aa126092d6ee4b5c22a42c8abe0fe"),
    // Georgia Root Certificate Authority
    StaticString(
        "0x33630d016957de6e6a4b65b2827081886e520c84317af6af53846ceb2b1d7755"),
    // Maryland MVA, CN=Fast Enterprises Root
    StaticString(
        "0x3848f928aca98ecf71214e10be538591aedd776929c2b09b4afb0681160857bb"),
    // Maryland MVA, CN=Fast Enterprises Root
    StaticString(
        "0x79a63d1496b6ae12bf7946adeb5207b86a350927345248fcdcf3d781b24dc7c6"),
    // New Mexico Root Certificate Authority
    StaticString(
        "0x9b52d953a90b66dfcdf295663bc8304945915b751227516c0f5d1d563e914ab3"),
    // Google UL TESTING Staging DS
    StaticString(
        "0xb19764d33b530e9bd6a87081f1d8e5cbc80db640ac962ee8a3e0393c14c6bfb4"),
    // C=US
    StaticString(
        "0x8ab996be9c14dd5749bf9ad85d373f9bc4ee360428ef152823c6125b6c1fcb51"),
    // State Of Utopia Issuing Authority Signing Key
    StaticString(
        "0xb8555be0e8944c3f684b649cf2786a59fe7db6e73c27d4298fe903e3711a7017"),
};

static const char *kIDPass = "com.google.wallet.idcard.1";
static const char *kMDL = "org.iso.18013.5.1.mDL";

struct MdocTests {
  StaticString pkx, pky; /* public key of the issuer */
  uint8_t transcript[1024];
  size_t transcript_size;
  uint8_t *now;
  const char *doc_type;
  size_t mdoc_size;
  uint8_t mdoc[5000];
};

// These mdoc examples are used in several tests.
static const struct MdocTests mdoc_tests[] = {
    // First example only has "age_over_18"
    {StaticString(
         "0x2c80c10bf70f63bddcc41ea20d76a22ecba2a97fa8811bf19d572433b12c0c1f"),
     StaticString(
         "0x3f994c043be7e17dd08387281bac0c37a529361b3cb36a0fac38d41ac066f903"),
     {0x83, 0xf6, 0xf6, 0x84, 0x71, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
      0x48, 0x61, 0x6e, 0x64, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x31, 0x58, 0x20,
      0x2e, 0x10, 0x05, 0xb3, 0xa9, 0xc8, 0xf0, 0xdf, 0x04, 0xdb, 0x42, 0x30,
      0x01, 0xc8, 0xb5, 0x39, 0x03, 0xfe, 0xd0, 0x71, 0xba, 0x50, 0x24, 0xc3,
      0xba, 0x69, 0x74, 0x0e, 0x62, 0xd4, 0x91, 0x7e, 0x58, 0x19, 0x63, 0x6f,
      0x6d, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x64,
      0x6c, 0x2e, 0x61, 0x70, 0x70, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x58,
      0x20, 0xd8, 0xe7, 0x3c, 0x70, 0x60, 0xe3, 0xe8, 0x0d, 0x3d, 0xef, 0xc2,
      0x63, 0x4e, 0xb0, 0x4d, 0x08, 0xc6, 0x56, 0xe2, 0x60, 0x68, 0xd8, 0xa5,
      0x63, 0xf5, 0xb9, 0x45, 0x85, 0xda, 0xe1, 0x4f, 0xad},
     117,
     (uint8_t *)"2024-01-30T09:00:00Z",
     kMDL,
     1452,
     {0xa3, 0x67, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e,
      0x30, 0x69, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x81,
      0xa3, 0x67, 0x64, 0x6f, 0x63, 0x54, 0x79, 0x70, 0x65, 0x75, 0x6f, 0x72,
      0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
      0x35, 0x2e, 0x31, 0x2e, 0x6d, 0x44, 0x4c, 0x6c, 0x69, 0x73, 0x73, 0x75,
      0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xa2, 0x6a, 0x6e, 0x61,
      0x6d, 0x65, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0xa1, 0x71, 0x6f, 0x72,
      0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
      0x35, 0x2e, 0x31, 0x81, 0xd8, 0x18, 0x58, 0x4f, 0xa4, 0x68, 0x64, 0x69,
      0x67, 0x65, 0x73, 0x74, 0x49, 0x44, 0x00, 0x66, 0x72, 0x61, 0x6e, 0x64,
      0x6f, 0x6d, 0x50, 0xf1, 0x10, 0x59, 0xeb, 0x6f, 0xbc, 0xe6, 0x26, 0x55,
      0xdf, 0xbd, 0x6f, 0x83, 0xb8, 0x96, 0x70, 0x71, 0x65, 0x6c, 0x65, 0x6d,
      0x65, 0x6e, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65,
      0x72, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x31,
      0x38, 0x6c, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c,
      0x75, 0x65, 0xf5, 0x6a, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x41, 0x75,
      0x74, 0x68, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa1, 0x18, 0x21, 0x59, 0x02,
      0x27, 0x30, 0x82, 0x02, 0x23, 0x30, 0x82, 0x01, 0xa9, 0xa0, 0x03, 0x02,
      0x01, 0x02, 0x02, 0x10, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x5f, 0x54,
      0x65, 0x73, 0x74, 0x5f, 0x44, 0x53, 0x5f, 0x31, 0x30, 0x0a, 0x06, 0x08,
      0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03, 0x30, 0x3c, 0x31, 0x0b,
      0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
      0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x05, 0x55, 0x53,
      0x2d, 0x4d, 0x41, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x03,
      0x0c, 0x14, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x45, 0x53,
      0x54, 0x20, 0x49, 0x41, 0x43, 0x41, 0x20, 0x6d, 0x44, 0x4c, 0x30, 0x1e,
      0x17, 0x0d, 0x32, 0x33, 0x30, 0x37, 0x32, 0x36, 0x30, 0x30, 0x30, 0x30,
      0x30, 0x31, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x31, 0x30, 0x32, 0x35, 0x30,
      0x30, 0x30, 0x30, 0x30, 0x31, 0x5a, 0x30, 0x3a, 0x31, 0x0b, 0x30, 0x09,
      0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0e, 0x30,
      0x0c, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x05, 0x55, 0x53, 0x2d, 0x4d,
      0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x12,
      0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x45, 0x53, 0x54, 0x20,
      0x44, 0x53, 0x20, 0x6d, 0x44, 0x4c, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
      0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
      0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x2c, 0x80, 0xc1,
      0x0b, 0xf7, 0x0f, 0x63, 0xbd, 0xdc, 0xc4, 0x1e, 0xa2, 0x0d, 0x76, 0xa2,
      0x2e, 0xcb, 0xa2, 0xa9, 0x7f, 0xa8, 0x81, 0x1b, 0xf1, 0x9d, 0x57, 0x24,
      0x33, 0xb1, 0x2c, 0x0c, 0x1f, 0x3f, 0x99, 0x4c, 0x04, 0x3b, 0xe7, 0xe1,
      0x7d, 0xd0, 0x83, 0x87, 0x28, 0x1b, 0xac, 0x0c, 0x37, 0xa5, 0x29, 0x36,
      0x1b, 0x3c, 0xb3, 0x6a, 0x0f, 0xac, 0x38, 0xd4, 0x1a, 0xc0, 0x66, 0xf9,
      0x03, 0xa3, 0x81, 0x8e, 0x30, 0x81, 0x8b, 0x30, 0x1f, 0x06, 0x03, 0x55,
      0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xde, 0xf3, 0xab, 0x6d,
      0x37, 0xde, 0x9e, 0x3a, 0x81, 0x6e, 0x10, 0x32, 0xd0, 0x2b, 0x48, 0xaf,
      0x35, 0x8a, 0x71, 0xab, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
      0x16, 0x04, 0x14, 0x39, 0x7b, 0xe5, 0x3c, 0x50, 0xdf, 0xf0, 0xd7, 0xa2,
      0xe4, 0xa9, 0xe4, 0x65, 0xea, 0x73, 0x7a, 0x9e, 0xd8, 0x92, 0xc8, 0x30,
      0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
      0x02, 0x07, 0x80, 0x30, 0x22, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, 0x1b,
      0x30, 0x19, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
      0x77, 0x77, 0x77, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63,
      0x6f, 0x6d, 0x2f, 0x30, 0x15, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01,
      0xff, 0x04, 0x0b, 0x30, 0x09, 0x06, 0x07, 0x28, 0x81, 0x8c, 0x5d, 0x05,
      0x01, 0x02, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04,
      0x03, 0x03, 0x03, 0x68, 0x00, 0x30, 0x65, 0x02, 0x31, 0x00, 0xa2, 0x5c,
      0xdb, 0x8a, 0x6f, 0x0c, 0x23, 0x90, 0x5b, 0x9d, 0x3f, 0x58, 0x2a, 0x60,
      0x8f, 0x0c, 0x16, 0xf4, 0x36, 0xb5, 0xee, 0xe3, 0x58, 0x7d, 0x2c, 0xa9,
      0x77, 0x16, 0x7c, 0x6f, 0x16, 0xa2, 0xb9, 0xde, 0x10, 0xfe, 0x1d, 0xdd,
      0x16, 0xde, 0x07, 0x2e, 0x4f, 0x86, 0x6f, 0x06, 0x05, 0xa7, 0x02, 0x30,
      0x6a, 0x48, 0x89, 0x42, 0x1f, 0x9b, 0xb3, 0x74, 0xb8, 0xbd, 0xe1, 0x75,
      0xb3, 0xd6, 0xab, 0x23, 0x21, 0x6b, 0xa9, 0x7e, 0x68, 0x95, 0x10, 0x97,
      0x4a, 0xe0, 0xf6, 0x59, 0xa1, 0x0e, 0x95, 0x84, 0x69, 0xb5, 0xbd, 0x80,
      0xdf, 0xc1, 0x05, 0x88, 0x83, 0x1d, 0x93, 0x19, 0x81, 0xa6, 0x39, 0x98,
      0x59, 0x01, 0xe8, 0xd8, 0x18, 0x59, 0x01, 0xe3, 0xa6, 0x67, 0x76, 0x65,
      0x72, 0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e, 0x30, 0x6f, 0x64, 0x69,
      0x67, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
      0x6d, 0x67, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x67, 0x64, 0x6f,
      0x63, 0x54, 0x79, 0x70, 0x65, 0x75, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73,
      0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0x2e,
      0x6d, 0x44, 0x4c, 0x6c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x69, 0x67,
      0x65, 0x73, 0x74, 0x73, 0xa1, 0x71, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73,
      0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0xa5,
      0x01, 0x58, 0x20, 0xad, 0xf6, 0xa3, 0x33, 0x03, 0x6a, 0xde, 0xfc, 0x48,
      0x90, 0xdf, 0x38, 0xe0, 0xf7, 0x37, 0x22, 0x90, 0x85, 0xa9, 0xb0, 0xba,
      0x7c, 0x07, 0x19, 0xd3, 0x92, 0x40, 0x5d, 0x74, 0x46, 0x23, 0x77, 0x02,
      0x58, 0x20, 0xa0, 0xa1, 0x4a, 0x5a, 0xa1, 0xb3, 0x36, 0x84, 0x4d, 0x8f,
      0x8d, 0x14, 0x8e, 0xd4, 0x4f, 0xd2, 0xcc, 0xc6, 0x6f, 0x54, 0xd8, 0x78,
      0x2b, 0x70, 0xfb, 0x77, 0x13, 0xfb, 0x3c, 0x93, 0xf5, 0x56, 0x03, 0x58,
      0x20, 0x97, 0xb0, 0x18, 0x4e, 0xdd, 0xe3, 0x99, 0xcb, 0x7d, 0xea, 0x2d,
      0x7d, 0x27, 0x9a, 0x45, 0x69, 0x90, 0xd9, 0xf3, 0x12, 0x46, 0x71, 0x63,
      0x78, 0x7e, 0x1b, 0xa7, 0x66, 0x0a, 0x5c, 0x08, 0x6f, 0x04, 0x58, 0x20,
      0xaf, 0x0b, 0x9f, 0xe7, 0x24, 0x5c, 0xa9, 0xa5, 0x9f, 0x64, 0xb1, 0xaa,
      0x82, 0xcc, 0x2c, 0x1a, 0xb1, 0x38, 0x6f, 0x77, 0x95, 0x64, 0x93, 0x83,
      0x62, 0x97, 0xc8, 0xa8, 0x4d, 0x2a, 0xe0, 0xb4, 0x00, 0x58, 0x20, 0x0d,
      0x98, 0x54, 0xdb, 0x51, 0x48, 0x6f, 0xf4, 0x49, 0x07, 0xbc, 0x61, 0x4f,
      0xfa, 0xea, 0x93, 0xda, 0xe1, 0xa8, 0x9e, 0xad, 0x40, 0x26, 0x3f, 0x90,
      0x1a, 0xe6, 0xce, 0x41, 0x26, 0x46, 0x21, 0x6d, 0x64, 0x65, 0x76, 0x69,
      0x63, 0x65, 0x4b, 0x65, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0xa1, 0x69, 0x64,
      0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0xa4, 0x01, 0x02, 0x20,
      0x01, 0x21, 0x58, 0x20, 0xc3, 0x14, 0xa7, 0xab, 0xba, 0x07, 0xe4, 0x0e,
      0x64, 0xae, 0x87, 0xdb, 0x4a, 0xd9, 0x71, 0x80, 0x13, 0xfd, 0x39, 0x8e,
      0x6e, 0x23, 0x17, 0xb3, 0x04, 0xf5, 0x7f, 0xc9, 0xac, 0xca, 0xb9, 0xf5,
      0x22, 0x58, 0x20, 0xed, 0xb8, 0xb0, 0x23, 0x0c, 0xcc, 0x98, 0xdd, 0x42,
      0xcd, 0xff, 0x89, 0xa8, 0xd1, 0xe2, 0x5f, 0xf8, 0xd1, 0xa7, 0xfa, 0x38,
      0x9e, 0x92, 0xdc, 0x8f, 0x01, 0xaf, 0x98, 0x5a, 0x79, 0xef, 0xcc, 0x6c,
      0x76, 0x61, 0x6c, 0x69, 0x64, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f,
      0xa3, 0x66, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xc0, 0x74, 0x32, 0x30,
      0x32, 0x34, 0x2d, 0x30, 0x31, 0x2d, 0x32, 0x35, 0x54, 0x32, 0x31, 0x3a,
      0x31, 0x32, 0x3a, 0x35, 0x39, 0x5a, 0x69, 0x76, 0x61, 0x6c, 0x69, 0x64,
      0x46, 0x72, 0x6f, 0x6d, 0xc0, 0x74, 0x32, 0x30, 0x32, 0x34, 0x2d, 0x30,
      0x31, 0x2d, 0x32, 0x35, 0x54, 0x32, 0x31, 0x3a, 0x31, 0x32, 0x3a, 0x35,
      0x39, 0x5a, 0x6a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69,
      0x6c, 0xc0, 0x74, 0x32, 0x30, 0x32, 0x34, 0x2d, 0x30, 0x32, 0x2d, 0x32,
      0x34, 0x54, 0x32, 0x31, 0x3a, 0x31, 0x32, 0x3a, 0x35, 0x39, 0x5a, 0x58,
      0x40, 0x67, 0x00, 0x7b, 0xe9, 0x95, 0xa3, 0xe7, 0x43, 0x94, 0x5c, 0x4f,
      0xb1, 0xc5, 0x8a, 0xe8, 0x9d, 0x72, 0xd4, 0x97, 0x31, 0x82, 0x51, 0xd9,
      0xf1, 0x97, 0x4f, 0x08, 0xe5, 0x13, 0x89, 0xc8, 0x6d, 0x40, 0x11, 0x01,
      0x48, 0x8f, 0xc3, 0x7e, 0x66, 0x55, 0x46, 0x49, 0x53, 0xc5, 0x1c, 0x4f,
      0x8c, 0x64, 0xb9, 0x60, 0x13, 0xa1, 0xab, 0x04, 0xa9, 0xa6, 0x5b, 0x80,
      0xfd, 0x2f, 0x3b, 0x5c, 0xa3, 0x6c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
      0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xa2, 0x6a, 0x6e, 0x61, 0x6d, 0x65,
      0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0xd8, 0x18, 0x41, 0xa0, 0x6a, 0x64,
      0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0xa1, 0x6f, 0x64,
      0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
      0x72, 0x65, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0xf6, 0x58, 0x40, 0xcf,
      0xb0, 0xac, 0x1a, 0x63, 0x1b, 0x06, 0xe1, 0xb5, 0xd5, 0x03, 0x16, 0xfa,
      0x43, 0xda, 0xb2, 0x38, 0x86, 0xa2, 0x6b, 0x67, 0x6c, 0x33, 0xba, 0xb4,
      0x52, 0x63, 0x4c, 0xba, 0xdf, 0xd1, 0xb3, 0xab, 0xd1, 0xbd, 0x7a, 0x5e,
      0x3c, 0x36, 0x97, 0xde, 0x7c, 0xe1, 0xfc, 0x00, 0x4a, 0x90, 0xde, 0x84,
      0xe0, 0x62, 0x64, 0xfe, 0x95, 0x78, 0x81, 0x98, 0xac, 0xdc, 0x3e, 0x4c,
      0x57, 0x5d, 0x59, 0x66, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x00}},
    {
        StaticString("0x2c80c10bf70f63bddcc41ea20d76a22ecba2a97fa8811bf19d57243"
                     "3b12c0c1f"),
        StaticString("0x3f994c043be7e17dd08387281bac0c37a529361b3cb36a0fac38d41"
                     "ac066f903"),
        {0x83, 0xf6, 0xf6, 0x84, 0x71, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
         0x48, 0x61, 0x6e, 0x64, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x31, 0x58, 0x20,
         0x2e, 0x10, 0x05, 0xb3, 0xa9, 0xc8, 0xf0, 0xdf, 0x04, 0xdb, 0x42, 0x30,
         0x01, 0xc8, 0xb5, 0x39, 0x03, 0xfe, 0xd0, 0x71, 0xba, 0x50, 0x24, 0xc3,
         0xba, 0x69, 0x74, 0x0e, 0x62, 0xd4, 0x91, 0x7e, 0x58, 0x19, 0x63, 0x6f,
         0x6d, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x64,
         0x6c, 0x2e, 0x61, 0x70, 0x70, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x58,
         0x20, 0xd8, 0xe7, 0x3c, 0x70, 0x60, 0xe3, 0xe8, 0x0d, 0x3d, 0xef, 0xc2,
         0x63, 0x4e, 0xb0, 0x4d, 0x08, 0xc6, 0x56, 0xe2, 0x60, 0x68, 0xd8, 0xa5,
         0x63, 0xf5, 0xb9, 0x45, 0x85, 0xda, 0xe1, 0x4f, 0xad},
        117,
        (uint8_t *)"2024-01-31T09:00:00Z",
        kMDL,
        1488,
        {0xa3, 0x67, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e,
         0x30, 0x69, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x81,
         0xa3, 0x67, 0x64, 0x6f, 0x63, 0x54, 0x79, 0x70, 0x65, 0x75, 0x6f, 0x72,
         0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
         0x35, 0x2e, 0x31, 0x2e, 0x6d, 0x44, 0x4c, 0x6c, 0x69, 0x73, 0x73, 0x75,
         0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xa2, 0x6a, 0x6e, 0x61,
         0x6d, 0x65, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0xa1, 0x71, 0x6f, 0x72,
         0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
         0x35, 0x2e, 0x31, 0x81, 0xd8, 0x18, 0x58, 0x4f, 0xa4, 0x68, 0x64, 0x69,
         0x67, 0x65, 0x73, 0x74, 0x49, 0x44, 0x02, 0x66, 0x72, 0x61, 0x6e, 0x64,
         0x6f, 0x6d, 0x50, 0x52, 0xd8, 0x3d, 0xee, 0xdc, 0x0b, 0xf6, 0x62, 0x05,
         0x83, 0x5c, 0xcc, 0x16, 0xad, 0x68, 0x2d, 0x71, 0x65, 0x6c, 0x65, 0x6d,
         0x65, 0x6e, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65,
         0x72, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x31,
         0x38, 0x6c, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c,
         0x75, 0x65, 0xf5, 0x6a, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x41, 0x75,
         0x74, 0x68, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa1, 0x18, 0x21, 0x59, 0x02,
         0x27, 0x30, 0x82, 0x02, 0x23, 0x30, 0x82, 0x01, 0xa9, 0xa0, 0x03, 0x02,
         0x01, 0x02, 0x02, 0x10, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x5f, 0x54,
         0x65, 0x73, 0x74, 0x5f, 0x44, 0x53, 0x5f, 0x31, 0x30, 0x0a, 0x06, 0x08,
         0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03, 0x30, 0x3c, 0x31, 0x0b,
         0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
         0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x05, 0x55, 0x53,
         0x2d, 0x4d, 0x41, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x03,
         0x0c, 0x14, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x45, 0x53,
         0x54, 0x20, 0x49, 0x41, 0x43, 0x41, 0x20, 0x6d, 0x44, 0x4c, 0x30, 0x1e,
         0x17, 0x0d, 0x32, 0x33, 0x30, 0x37, 0x32, 0x36, 0x30, 0x30, 0x30, 0x30,
         0x30, 0x31, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x31, 0x30, 0x32, 0x35, 0x30,
         0x30, 0x30, 0x30, 0x30, 0x31, 0x5a, 0x30, 0x3a, 0x31, 0x0b, 0x30, 0x09,
         0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0e, 0x30,
         0x0c, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x05, 0x55, 0x53, 0x2d, 0x4d,
         0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x12,
         0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x45, 0x53, 0x54, 0x20,
         0x44, 0x53, 0x20, 0x6d, 0x44, 0x4c, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
         0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
         0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x2c, 0x80, 0xc1,
         0x0b, 0xf7, 0x0f, 0x63, 0xbd, 0xdc, 0xc4, 0x1e, 0xa2, 0x0d, 0x76, 0xa2,
         0x2e, 0xcb, 0xa2, 0xa9, 0x7f, 0xa8, 0x81, 0x1b, 0xf1, 0x9d, 0x57, 0x24,
         0x33, 0xb1, 0x2c, 0x0c, 0x1f, 0x3f, 0x99, 0x4c, 0x04, 0x3b, 0xe7, 0xe1,
         0x7d, 0xd0, 0x83, 0x87, 0x28, 0x1b, 0xac, 0x0c, 0x37, 0xa5, 0x29, 0x36,
         0x1b, 0x3c, 0xb3, 0x6a, 0x0f, 0xac, 0x38, 0xd4, 0x1a, 0xc0, 0x66, 0xf9,
         0x03, 0xa3, 0x81, 0x8e, 0x30, 0x81, 0x8b, 0x30, 0x1f, 0x06, 0x03, 0x55,
         0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xde, 0xf3, 0xab, 0x6d,
         0x37, 0xde, 0x9e, 0x3a, 0x81, 0x6e, 0x10, 0x32, 0xd0, 0x2b, 0x48, 0xaf,
         0x35, 0x8a, 0x71, 0xab, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
         0x16, 0x04, 0x14, 0x39, 0x7b, 0xe5, 0x3c, 0x50, 0xdf, 0xf0, 0xd7, 0xa2,
         0xe4, 0xa9, 0xe4, 0x65, 0xea, 0x73, 0x7a, 0x9e, 0xd8, 0x92, 0xc8, 0x30,
         0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
         0x02, 0x07, 0x80, 0x30, 0x22, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, 0x1b,
         0x30, 0x19, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
         0x77, 0x77, 0x77, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63,
         0x6f, 0x6d, 0x2f, 0x30, 0x15, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01,
         0xff, 0x04, 0x0b, 0x30, 0x09, 0x06, 0x07, 0x28, 0x81, 0x8c, 0x5d, 0x05,
         0x01, 0x02, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04,
         0x03, 0x03, 0x03, 0x68, 0x00, 0x30, 0x65, 0x02, 0x31, 0x00, 0xa2, 0x5c,
         0xdb, 0x8a, 0x6f, 0x0c, 0x23, 0x90, 0x5b, 0x9d, 0x3f, 0x58, 0x2a, 0x60,
         0x8f, 0x0c, 0x16, 0xf4, 0x36, 0xb5, 0xee, 0xe3, 0x58, 0x7d, 0x2c, 0xa9,
         0x77, 0x16, 0x7c, 0x6f, 0x16, 0xa2, 0xb9, 0xde, 0x10, 0xfe, 0x1d, 0xdd,
         0x16, 0xde, 0x07, 0x2e, 0x4f, 0x86, 0x6f, 0x06, 0x05, 0xa7, 0x02, 0x30,
         0x6a, 0x48, 0x89, 0x42, 0x1f, 0x9b, 0xb3, 0x74, 0xb8, 0xbd, 0xe1, 0x75,
         0xb3, 0xd6, 0xab, 0x23, 0x21, 0x6b, 0xa9, 0x7e, 0x68, 0x95, 0x10, 0x97,
         0x4a, 0xe0, 0xf6, 0x59, 0xa1, 0x0e, 0x95, 0x84, 0x69, 0xb5, 0xbd, 0x80,
         0xdf, 0xc1, 0x05, 0x88, 0x83, 0x1d, 0x93, 0x19, 0x81, 0xa6, 0x39, 0x98,
         0x59, 0x02, 0x0b, 0xd8, 0x18, 0x59, 0x02, 0x06, 0xa6, 0x67, 0x76, 0x65,
         0x72, 0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e, 0x30, 0x6f, 0x64, 0x69,
         0x67, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
         0x6d, 0x67, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x67, 0x64, 0x6f,
         0x63, 0x54, 0x79, 0x70, 0x65, 0x75, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73,
         0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0x2e,
         0x6d, 0x44, 0x4c, 0x6c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x69, 0x67,
         0x65, 0x73, 0x74, 0x73, 0xa1, 0x71, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73,
         0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0xa6,
         0x01, 0x58, 0x20, 0x6b, 0x6e, 0xe3, 0xd4, 0x97, 0x13, 0x66, 0xb2, 0xda,
         0x89, 0x5f, 0x8b, 0x79, 0xb0, 0x08, 0x35, 0xd4, 0xeb, 0x9e, 0x36, 0x7b,
         0xa2, 0x00, 0x21, 0x06, 0x39, 0xc9, 0xa2, 0x24, 0xd5, 0x61, 0x91, 0x00,
         0x58, 0x20, 0x97, 0x1e, 0x94, 0xb3, 0x34, 0x56, 0xbb, 0x45, 0xd1, 0x7e,
         0x9a, 0xe5, 0x58, 0xa3, 0x51, 0x5f, 0x68, 0xb4, 0x66, 0x27, 0x22, 0xb2,
         0xee, 0x3c, 0x20, 0x98, 0x91, 0x52, 0xc2, 0xad, 0xc0, 0x71, 0x04, 0x58,
         0x20, 0x64, 0x0a, 0x32, 0xae, 0x3e, 0xea, 0x27, 0x30, 0xe1, 0xdb, 0x11,
         0x27, 0x56, 0xec, 0x65, 0x00, 0xa2, 0xab, 0xee, 0x2b, 0xe6, 0xe7, 0x50,
         0xe2, 0x3c, 0x38, 0xb7, 0x55, 0x27, 0xf1, 0x8f, 0x37, 0x05, 0x58, 0x20,
         0x28, 0x7e, 0x50, 0x4b, 0xd3, 0x5c, 0x0f, 0x8f, 0xa8, 0x23, 0x91, 0x66,
         0x50, 0xd3, 0xc8, 0xa9, 0x8d, 0x38, 0x41, 0x50, 0xe7, 0x95, 0xf6, 0x2a,
         0x11, 0x44, 0xda, 0x45, 0xc0, 0x91, 0x21, 0x3b, 0x03, 0x58, 0x20, 0xd1,
         0x7f, 0x88, 0xf6, 0x17, 0x79, 0xaa, 0xc0, 0xa1, 0x00, 0xd0, 0xdd, 0xa0,
         0xdd, 0x45, 0x85, 0xbd, 0x7b, 0x2c, 0xe5, 0x99, 0x22, 0x47, 0xf3, 0xed,
         0x4f, 0xdc, 0xd7, 0x1d, 0x9c, 0xfa, 0xcc, 0x02, 0x58, 0x20, 0x96, 0x22,
         0xa2, 0xd9, 0xfe, 0x02, 0xec, 0xbe, 0x3c, 0x63, 0x1d, 0x9a, 0xbb, 0x94,
         0x05, 0xf5, 0x08, 0xed, 0xd0, 0xf4, 0x3e, 0x5f, 0x12, 0x51, 0xc9, 0xd7,
         0xf8, 0x79, 0xe6, 0x96, 0xcb, 0x60, 0x6d, 0x64, 0x65, 0x76, 0x69, 0x63,
         0x65, 0x4b, 0x65, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0xa1, 0x69, 0x64, 0x65,
         0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0xa4, 0x01, 0x02, 0x20, 0x01,
         0x21, 0x58, 0x20, 0xc5, 0x4e, 0x80, 0xbd, 0xa8, 0xa1, 0xe1, 0xba, 0x92,
         0x66, 0xcb, 0xe7, 0x4c, 0x7a, 0x73, 0xcf, 0x79, 0x08, 0xe2, 0x98, 0x58,
         0x23, 0xc9, 0x1a, 0x9c, 0xb1, 0xfe, 0x57, 0x98, 0x52, 0x9d, 0xfb, 0x22,
         0x58, 0x20, 0x1a, 0xc3, 0x2f, 0x5c, 0x02, 0x4d, 0xd7, 0xa7, 0x86, 0x1d,
         0x2a, 0xb8, 0x97, 0x54, 0x3a, 0x1e, 0x17, 0x97, 0x04, 0x77, 0xab, 0xd3,
         0x88, 0x63, 0xa5, 0xb8, 0xde, 0x22, 0x33, 0xd1, 0xdb, 0x95, 0x6c, 0x76,
         0x61, 0x6c, 0x69, 0x64, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0xa3,
         0x66, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xc0, 0x74, 0x32, 0x30, 0x32,
         0x34, 0x2d, 0x30, 0x31, 0x2d, 0x32, 0x36, 0x54, 0x31, 0x35, 0x3a, 0x33,
         0x32, 0x3a, 0x35, 0x30, 0x5a, 0x69, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x46,
         0x72, 0x6f, 0x6d, 0xc0, 0x74, 0x32, 0x30, 0x32, 0x34, 0x2d, 0x30, 0x31,
         0x2d, 0x32, 0x36, 0x54, 0x31, 0x35, 0x3a, 0x33, 0x32, 0x3a, 0x35, 0x30,
         0x5a, 0x6a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c,
         0xc0, 0x74, 0x32, 0x30, 0x32, 0x34, 0x2d, 0x30, 0x32, 0x2d, 0x32, 0x35,
         0x54, 0x31, 0x35, 0x3a, 0x33, 0x32, 0x3a, 0x35, 0x30, 0x5a, 0x58, 0x40,
         0xac, 0x87, 0xaa, 0x02, 0x60, 0xb8, 0x36, 0x22, 0x9d, 0x1e, 0xa2, 0x85,
         0x2f, 0x3c, 0xa3, 0x2a, 0xa3, 0x63, 0x9d, 0x26, 0x0c, 0x00, 0xda, 0x81,
         0x24, 0x22, 0xbb, 0xd4, 0x5f, 0xc2, 0x05, 0x37, 0x08, 0xff, 0x70, 0x50,
         0xde, 0xcc, 0x2d, 0xfc, 0xfd, 0x3a, 0xba, 0xfe, 0xea, 0x3d, 0x68, 0xe5,
         0x14, 0xb5, 0x24, 0xe4, 0xc2, 0xee, 0xf8, 0xad, 0x28, 0xec, 0xd3, 0x14,
         0x21, 0xfb, 0x9f, 0x8f, 0x6c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53,
         0x69, 0x67, 0x6e, 0x65, 0x64, 0xa2, 0x6a, 0x6e, 0x61, 0x6d, 0x65, 0x53,
         0x70, 0x61, 0x63, 0x65, 0x73, 0xd8, 0x18, 0x41, 0xa0, 0x6a, 0x64, 0x65,
         0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0xa1, 0x6f, 0x64, 0x65,
         0x76, 0x69, 0x63, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
         0x65, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0xf6, 0x58, 0x40, 0xf2, 0xa2,
         0x59, 0xcc, 0x16, 0xbf, 0x5c, 0x30, 0x52, 0x68, 0x4b, 0x1f, 0x16, 0x62,
         0x07, 0xd5, 0x93, 0xff, 0x5b, 0x0e, 0x1d, 0xa8, 0xc1, 0x44, 0xc0, 0xb6,
         0xe9, 0x0d, 0x53, 0x7b, 0xde, 0x49, 0x39, 0xe7, 0xe8, 0xa0, 0xfa, 0x24,
         0x70, 0x8e, 0x5a, 0x59, 0x24, 0xb9, 0x53, 0x52, 0xd6, 0x4f, 0x02, 0x0b,
         0x59, 0x89, 0x97, 0x1f, 0x09, 0x1c, 0xa1, 0xfe, 0x05, 0xe2, 0x5b, 0x45,
         0x9d, 0x86, 0x66, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x00},
    },
    // Canonical payground example
    {StaticString(
         "0xcc900843699408936c912474a27abb7b62d7c90e4f6d8557714825476eee2366"),
     StaticString(
         "0xb19764d33b530e9bd6a87081f1d8e5cbc80db640ac962ee8a3e0393c14c6bfb4"),
     {0x83, 0xf6, 0xf6, 0x84, 0x76, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x57,
      0x61, 0x6c, 0x6c, 0x65, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6f, 0x76, 0x65,
      0x72, 0x76, 0x31, 0x44, 0x01, 0x02, 0x03, 0x04, 0x78, 0x22, 0x63, 0x6f,
      0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x6e, 0x64,
      0x72, 0x6f, 0x69, 0x64, 0x2e, 0x67, 0x6d, 0x73, 0x2e, 0x74, 0x65, 0x73,
      0x74, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x58, 0x20, 0xb0, 0xd3,
      0x81, 0x79, 0x7d, 0x0a, 0x06, 0x4b, 0x5e, 0xbe, 0xcf, 0xfd, 0x9c, 0x8b,
      0x0f, 0x12, 0x88, 0xfc, 0x83, 0xb8, 0x59, 0xc7, 0x60, 0x8e, 0x91, 0x05,
      0x14, 0x30, 0x25, 0x51, 0x2c, 0x54},
     102,
     (uint8_t *)"2024-05-05T09:00:00Z",
     kMDL,
     3272,
     {0xa3, 0x67, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e,
      0x30, 0x69, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x81,
      0xa3, 0x67, 0x64, 0x6f, 0x63, 0x54, 0x79, 0x70, 0x65, 0x75, 0x6f, 0x72,
      0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
      0x35, 0x2e, 0x31, 0x2e, 0x6d, 0x44, 0x4c, 0x6c, 0x69, 0x73, 0x73, 0x75,
      0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xa2, 0x6a, 0x6e, 0x61,
      0x6d, 0x65, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0xa1, 0x71, 0x6f, 0x72,
      0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
      0x35, 0x2e, 0x31, 0x81, 0xd8, 0x18, 0x58, 0x61, 0xa4, 0x68, 0x64, 0x69,
      0x67, 0x65, 0x73, 0x74, 0x49, 0x44, 0x18, 0x1d, 0x66, 0x72, 0x61, 0x6e,
      0x64, 0x6f, 0x6d, 0x58, 0x20, 0x3d, 0x02, 0x64, 0xc0, 0x07, 0x37, 0x5a,
      0x43, 0x62, 0x0c, 0x37, 0xc0, 0xcc, 0x5c, 0x87, 0x3e, 0x15, 0x32, 0xa5,
      0x2f, 0x5d, 0x32, 0x0e, 0x05, 0xc0, 0xfb, 0x17, 0x6c, 0x0c, 0xd9, 0xba,
      0xb1, 0x71, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x65,
      0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x6b, 0x61, 0x67, 0x65, 0x5f,
      0x6f, 0x76, 0x65, 0x72, 0x5f, 0x31, 0x38, 0x6c, 0x65, 0x6c, 0x65, 0x6d,
      0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0xf5, 0x6a, 0x69, 0x73,
      0x73, 0x75, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x84, 0x43, 0xa1, 0x01,
      0x26, 0xa1, 0x18, 0x21, 0x59, 0x02, 0xde, 0x30, 0x82, 0x02, 0xda, 0x30,
      0x82, 0x02, 0x3b, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14, 0x09, 0xbd,
      0x7c, 0x4d, 0x2d, 0x53, 0x1a, 0x2c, 0x79, 0xbf, 0x50, 0x36, 0x29, 0x40,
      0x8e, 0xe8, 0xfd, 0x40, 0xc8, 0x89, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86,
      0x48, 0xce, 0x3d, 0x04, 0x03, 0x04, 0x30, 0x40, 0x31, 0x0f, 0x30, 0x0d,
      0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c,
      0x65, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
      0x5a, 0x5a, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
      0x17, 0x55, 0x4c, 0x20, 0x54, 0x45, 0x53, 0x54, 0x49, 0x4e, 0x47, 0x20,
      0x53, 0x74, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x20, 0x52, 0x6f, 0x6f, 0x74,
      0x30, 0x1e, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x34, 0x32, 0x36, 0x31, 0x35,
      0x34, 0x39, 0x34, 0x35, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x34, 0x32,
      0x36, 0x31, 0x35, 0x34, 0x39, 0x34, 0x35, 0x5a, 0x30, 0x3e, 0x31, 0x0f,
      0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f,
      0x67, 0x6c, 0x65, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
      0x13, 0x02, 0x5a, 0x5a, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
      0x03, 0x0c, 0x15, 0x55, 0x4c, 0x20, 0x54, 0x45, 0x53, 0x54, 0x49, 0x4e,
      0x47, 0x20, 0x53, 0x74, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x20, 0x44, 0x53,
      0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
      0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
      0x42, 0x00, 0x04, 0xcc, 0x90, 0x08, 0x43, 0x69, 0x94, 0x08, 0x93, 0x6c,
      0x91, 0x24, 0x74, 0xa2, 0x7a, 0xbb, 0x7b, 0x62, 0xd7, 0xc9, 0x0e, 0x4f,
      0x6d, 0x85, 0x57, 0x71, 0x48, 0x25, 0x47, 0x6e, 0xee, 0x23, 0x66, 0xb1,
      0x97, 0x64, 0xd3, 0x3b, 0x53, 0x0e, 0x9b, 0xd6, 0xa8, 0x70, 0x81, 0xf1,
      0xd8, 0xe5, 0xcb, 0xc8, 0x0d, 0xb6, 0x40, 0xac, 0x96, 0x2e, 0xe8, 0xa3,
      0xe0, 0x39, 0x3c, 0x14, 0xc6, 0xbf, 0xb4, 0xa3, 0x82, 0x01, 0x13, 0x30,
      0x82, 0x01, 0x0f, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
      0x30, 0x16, 0x80, 0x14, 0x73, 0xa7, 0x04, 0x1e, 0x50, 0x81, 0x93, 0x9e,
      0xe0, 0xdb, 0xb7, 0x02, 0x8a, 0xe3, 0x71, 0x2f, 0xd0, 0xaa, 0x36, 0xc1,
      0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
      0x03, 0x02, 0x07, 0x80, 0x30, 0x21, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04,
      0x1a, 0x30, 0x18, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
      0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
      0x63, 0x6f, 0x6d, 0x30, 0x81, 0x82, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
      0x7b, 0x30, 0x79, 0x30, 0x77, 0xa0, 0x75, 0xa0, 0x73, 0x86, 0x71, 0x68,
      0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,
      0x65, 0x63, 0x61, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,
      0x36, 0x36, 0x30, 0x33, 0x63, 0x36, 0x34, 0x37, 0x2d, 0x30, 0x30, 0x30,
      0x30, 0x2d, 0x32, 0x39, 0x64, 0x34, 0x2d, 0x61, 0x65, 0x61, 0x36, 0x2d,
      0x66, 0x34, 0x66, 0x35, 0x65, 0x38, 0x30, 0x64, 0x31, 0x62, 0x39, 0x30,
      0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x67, 0x6f, 0x6f,
      0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
      0x34, 0x31, 0x64, 0x34, 0x33, 0x36, 0x38, 0x35, 0x39, 0x65, 0x34, 0x35,
      0x64, 0x62, 0x66, 0x33, 0x65, 0x35, 0x61, 0x39, 0x2f, 0x63, 0x72, 0x6c,
      0x2e, 0x63, 0x72, 0x6c, 0x30, 0x15, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01,
      0x01, 0xff, 0x04, 0x0b, 0x30, 0x09, 0x06, 0x07, 0x28, 0x81, 0x8c, 0x5d,
      0x05, 0x01, 0x02, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
      0x04, 0x14, 0x7c, 0xfd, 0x80, 0xe8, 0x38, 0x3f, 0x66, 0x5c, 0x9d, 0x6e,
      0x2b, 0xa3, 0xbe, 0xf6, 0x78, 0x05, 0xf6, 0x16, 0x51, 0x13, 0x30, 0x0a,
      0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04, 0x03, 0x81,
      0x8c, 0x00, 0x30, 0x81, 0x88, 0x02, 0x42, 0x01, 0xfb, 0x90, 0x69, 0xf7,
      0x3e, 0xc8, 0x67, 0x49, 0xfb, 0x2b, 0x12, 0x47, 0x2a, 0xbb, 0x43, 0x17,
      0x44, 0xf0, 0x88, 0x85, 0x00, 0x1a, 0xcb, 0x1c, 0xea, 0xb4, 0xeb, 0x57,
      0x5d, 0x34, 0xb1, 0x28, 0x44, 0x3d, 0xa5, 0x38, 0x11, 0x1b, 0x41, 0x0e,
      0xfe, 0x65, 0xea, 0xfb, 0xc2, 0xb4, 0x2f, 0x5c, 0x60, 0x83, 0x7c, 0x38,
      0xa0, 0x22, 0xe1, 0x98, 0x8c, 0x45, 0xe5, 0xf3, 0xb7, 0x87, 0x63, 0xa0,
      0x55, 0x02, 0x42, 0x00, 0xd0, 0x15, 0x2a, 0x35, 0x94, 0x15, 0x5f, 0xc4,
      0x59, 0x37, 0x90, 0x25, 0x07, 0x8d, 0x3a, 0xb1, 0x23, 0x2a, 0x13, 0x22,
      0x7f, 0x13, 0xf7, 0x49, 0xe3, 0x93, 0xdd, 0x2b, 0xcd, 0xb6, 0xf7, 0xda,
      0x00, 0xc4, 0xc2, 0xa5, 0x6e, 0x9c, 0x0c, 0x23, 0x9f, 0xa0, 0x74, 0x0b,
      0x22, 0x2b, 0x71, 0xc2, 0x27, 0xf4, 0xa8, 0x94, 0x62, 0x43, 0x58, 0x07,
      0x51, 0xa9, 0x3a, 0x8c, 0xbf, 0xed, 0xa1, 0x20, 0x88, 0x59, 0x08, 0x3c,
      0xd8, 0x18, 0x59, 0x08, 0x37, 0xa6, 0x67, 0x76, 0x65, 0x72, 0x73, 0x69,
      0x6f, 0x6e, 0x63, 0x31, 0x2e, 0x30, 0x6f, 0x64, 0x69, 0x67, 0x65, 0x73,
      0x74, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x67, 0x53,
      0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x6c, 0x76, 0x61, 0x6c, 0x75, 0x65,
      0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0xa2, 0x71, 0x6f, 0x72, 0x67,
      0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35,
      0x2e, 0x31, 0xb8, 0x2b, 0x00, 0x58, 0x20, 0x5b, 0xa2, 0x15, 0x53, 0xe6,
      0x43, 0xe5, 0x20, 0xab, 0xde, 0x97, 0x81, 0x41, 0x9b, 0xa3, 0x30, 0x9e,
      0x78, 0x69, 0x92, 0x25, 0xd5, 0x63, 0x5f, 0x4f, 0xe3, 0xa5, 0x01, 0xc7,
      0x87, 0x0b, 0x9c, 0x01, 0x58, 0x20, 0x63, 0x42, 0x51, 0x8e, 0xb0, 0x68,
      0x5a, 0x92, 0xe0, 0x4d, 0x2d, 0x8c, 0xf8, 0xfd, 0xa8, 0x8f, 0xad, 0x35,
      0xe1, 0x8d, 0x77, 0x83, 0x48, 0xc2, 0x2d, 0xf9, 0x81, 0x09, 0x63, 0x29,
      0x26, 0x40, 0x02, 0x58, 0x20, 0x49, 0xbe, 0x06, 0xcd, 0xf4, 0xb6, 0x1f,
      0xe1, 0x6a, 0xb3, 0x4b, 0x3d, 0xf5, 0x30, 0x8d, 0xff, 0x42, 0x2a, 0xed,
      0x4f, 0xd9, 0xdb, 0xdb, 0x0e, 0x82, 0x0d, 0xad, 0xf7, 0xae, 0x1d, 0xcf,
      0x9e, 0x03, 0x58, 0x20, 0xc6, 0x4d, 0x57, 0x38, 0x99, 0x26, 0x59, 0xe5,
      0xd2, 0x3c, 0x79, 0xaa, 0xfc, 0xde, 0x6a, 0xa8, 0x9f, 0xa7, 0x51, 0x7a,
      0x6e, 0x63, 0xd4, 0x73, 0xe8, 0xa0, 0x1b, 0xd4, 0x5f, 0xa3, 0xba, 0xe3,
      0x04, 0x58, 0x20, 0xa2, 0x5d, 0x81, 0xd8, 0x32, 0x72, 0xd3, 0x6d, 0x11,
      0x76, 0x8c, 0x02, 0xab, 0x10, 0x3a, 0x8b, 0xdc, 0xd6, 0xae, 0x3b, 0x32,
      0x5c, 0x51, 0x1a, 0xcd, 0x87, 0xdb, 0xe5, 0x64, 0xba, 0x61, 0x1a, 0x05,
      0x58, 0x20, 0x46, 0x6c, 0x84, 0x0e, 0x42, 0x2b, 0x42, 0x43, 0x6a, 0x9e,
      0xf6, 0x0a, 0xe8, 0xd5, 0x8b, 0x1c, 0x6e, 0x27, 0xab, 0xd3, 0xa8, 0x5e,
      0xfa, 0x08, 0x2d, 0xbb, 0xb9, 0x91, 0xa7, 0x71, 0xec, 0x47, 0x06, 0x58,
      0x20, 0x07, 0xb4, 0xcc, 0x54, 0xef, 0xd0, 0x1d, 0xb3, 0x22, 0x98, 0xe5,
      0x77, 0x8b, 0x61, 0x43, 0xd1, 0x65, 0xeb, 0xe1, 0x11, 0x65, 0x19, 0x4c,
      0x34, 0xed, 0x7b, 0x21, 0xe4, 0x51, 0x32, 0x53, 0x48, 0x07, 0x58, 0x20,
      0x74, 0x2f, 0xc6, 0xb9, 0x4d, 0x42, 0xf4, 0xb5, 0x09, 0x3b, 0x2e, 0x09,
      0xf0, 0xed, 0x05, 0x33, 0x70, 0x28, 0xc1, 0x02, 0xd8, 0x4b, 0x0b, 0x83,
      0xd6, 0x17, 0xb3, 0x5e, 0xeb, 0xe4, 0x7d, 0x6b, 0x08, 0x58, 0x20, 0xc5,
      0xc2, 0x6f, 0x0d, 0x3f, 0x59, 0x6f, 0x96, 0x16, 0x2e, 0xbe, 0xc7, 0xcc,
      0xae, 0xfb, 0xf8, 0xa2, 0x9d, 0x77, 0x21, 0x9d, 0x21, 0xd6, 0xf9, 0xc2,
      0xec, 0x62, 0xf4, 0x0f, 0x76, 0x32, 0x1e, 0x09, 0x58, 0x20, 0xec, 0xff,
      0x29, 0x8b, 0xb3, 0x04, 0x89, 0x10, 0x7d, 0x67, 0x8a, 0x22, 0xd1, 0x0c,
      0xc3, 0x00, 0x93, 0x96, 0xa0, 0xa4, 0xc8, 0x5b, 0x42, 0x03, 0x79, 0xc4,
      0x99, 0x25, 0xcd, 0x84, 0xce, 0x99, 0x0a, 0x58, 0x20, 0x83, 0x98, 0x6e,
      0x01, 0xd1, 0x26, 0xf0, 0x4d, 0x45, 0x1c, 0xf0, 0x83, 0xe7, 0x1e, 0x24,
      0x0e, 0x3a, 0x9e, 0xe2, 0x24, 0x06, 0x86, 0xf1, 0xc9, 0xc7, 0x79, 0xe7,
      0x09, 0x3e, 0x66, 0x30, 0x90, 0x0b, 0x58, 0x20, 0x5d, 0x9e, 0x05, 0x3d,
      0x1a, 0xf1, 0x86, 0xad, 0x9d, 0xf4, 0xc1, 0xeb, 0x6b, 0xec, 0x06, 0xaa,
      0xb5, 0xab, 0x1d, 0xf3, 0xfe, 0x43, 0x59, 0x7f, 0xe4, 0x92, 0x18, 0xcd,
      0x05, 0x81, 0x98, 0x99, 0x0c, 0x58, 0x20, 0x3a, 0xda, 0x36, 0xa4, 0xa7,
      0x34, 0xe3, 0xb8, 0x97, 0x3c, 0x17, 0x32, 0xdb, 0xcc, 0x71, 0x67, 0x88,
      0x38, 0x3b, 0x11, 0x54, 0xea, 0xf4, 0x81, 0x8b, 0x58, 0xbf, 0xd9, 0xf8,
      0x29, 0x83, 0x13, 0x0d, 0x58, 0x20, 0x29, 0x1b, 0x18, 0x71, 0xda, 0x79,
      0x78, 0xb9, 0xc4, 0xe5, 0x2b, 0x1a, 0x6d, 0xfc, 0x14, 0x29, 0x74, 0x72,
      0x9d, 0xe8, 0x2f, 0x14, 0x96, 0x44, 0x0a, 0x53, 0x72, 0xb7, 0x19, 0x4c,
      0x8d, 0x4e, 0x0e, 0x58, 0x20, 0x47, 0x4b, 0x99, 0x28, 0x4b, 0xf4, 0xe7,
      0x13, 0x1c, 0x74, 0x9d, 0x85, 0x2c, 0xcb, 0xdf, 0x29, 0xa7, 0x26, 0x45,
      0x4b, 0x55, 0x96, 0x9e, 0x55, 0xfe, 0x0f, 0x47, 0xd4, 0x1a, 0xad, 0x5d,
      0xfb, 0x0f, 0x58, 0x20, 0xe0, 0x98, 0xdb, 0x13, 0x84, 0x30, 0xb2, 0x6c,
      0xc3, 0x3c, 0xb3, 0x15, 0xcc, 0x82, 0x0e, 0xc4, 0xea, 0x4b, 0x4b, 0x38,
      0xf7, 0x5a, 0x54, 0xe6, 0xa6, 0x4c, 0x7c, 0xf0, 0x9b, 0x5f, 0xf2, 0xc5,
      0x10, 0x58, 0x20, 0xfe, 0x68, 0xbd, 0x1d, 0xde, 0xf4, 0xc7, 0x60, 0xfd,
      0xf9, 0x6b, 0x19, 0xaf, 0x5f, 0x5a, 0x7f, 0x82, 0xc5, 0x26, 0xb9, 0xcc,
      0x66, 0xe4, 0xe3, 0x7a, 0x24, 0x3d, 0x33, 0x04, 0x91, 0x1f, 0x5c, 0x11,
      0x58, 0x20, 0x91, 0x36, 0x4e, 0x16, 0x85, 0x3f, 0xfe, 0x4f, 0xa1, 0x50,
      0x97, 0x87, 0xd8, 0x9d, 0xff, 0x12, 0x3b, 0x6b, 0x1f, 0xc4, 0x73, 0xe9,
      0xe0, 0xaa, 0x07, 0x0a, 0xce, 0xfc, 0xbf, 0x25, 0xf6, 0x9a, 0x12, 0x58,
      0x20, 0x89, 0xdb, 0x3e, 0x9b, 0xa2, 0x0e, 0xb0, 0x16, 0xd2, 0x80, 0xcd,
      0x2b, 0xe2, 0xe5, 0x86, 0xdb, 0xac, 0xc5, 0xc8, 0x3b, 0xb5, 0x10, 0x9e,
      0x8d, 0x01, 0xa3, 0xd2, 0x77, 0x1e, 0x6e, 0xa8, 0x1f, 0x13, 0x58, 0x20,
      0x9b, 0xb5, 0x36, 0x08, 0x50, 0x6c, 0xe0, 0x03, 0x3b, 0x88, 0x49, 0xef,
      0xd5, 0xeb, 0x15, 0x58, 0xe7, 0x14, 0xca, 0x05, 0x7a, 0x57, 0xa0, 0xcb,
      0x00, 0x78, 0x67, 0xd4, 0x3c, 0x8f, 0x6f, 0x79, 0x14, 0x58, 0x20, 0x33,
      0x3b, 0x9b, 0x28, 0x72, 0xbe, 0x3d, 0x76, 0xda, 0xeb, 0x79, 0x0e, 0x08,
      0x86, 0x55, 0xcc, 0x63, 0x78, 0x52, 0x17, 0x11, 0x57, 0x71, 0x9f, 0xe3,
      0x8d, 0x6c, 0xe9, 0xf7, 0xc3, 0x5e, 0x37, 0x15, 0x58, 0x20, 0xe9, 0xd4,
      0x87, 0x4e, 0x06, 0x4c, 0x64, 0xf0, 0x95, 0xff, 0xaf, 0xd3, 0xce, 0x33,
      0x2c, 0x6b, 0x5c, 0x31, 0xf1, 0x1e, 0xfe, 0x69, 0x6f, 0x3a, 0x9b, 0xbf,
      0x6c, 0x2a, 0x37, 0x69, 0x9f, 0xf7, 0x16, 0x58, 0x20, 0x66, 0xf2, 0x53,
      0xa4, 0xc5, 0xe0, 0xc9, 0x18, 0x5c, 0x29, 0x45, 0xee, 0x2d, 0x93, 0xc6,
      0x40, 0xfd, 0x1c, 0x55, 0xcf, 0xac, 0xf6, 0x44, 0xd8, 0xa7, 0x9b, 0x26,
      0xca, 0xc4, 0x32, 0xdf, 0x0c, 0x17, 0x58, 0x20, 0x79, 0xa3, 0x91, 0x0f,
      0xba, 0x84, 0x41, 0x91, 0x27, 0xa3, 0xcb, 0x41, 0xfc, 0x75, 0x51, 0x67,
      0xee, 0xf9, 0x9f, 0x6c, 0x32, 0x55, 0x9e, 0xbc, 0xe2, 0x5e, 0x81, 0x5d,
      0x6e, 0x0e, 0x7d, 0x18, 0x18, 0x18, 0x58, 0x20, 0x00, 0x2b, 0x44, 0x81,
      0x79, 0xd9, 0xde, 0xde, 0xb6, 0x72, 0xb1, 0x2a, 0xbd, 0xd1, 0xed, 0xfe,
      0xcf, 0xf4, 0x3d, 0x30, 0x31, 0xa8, 0x93, 0x97, 0xd7, 0x6d, 0x26, 0x3e,
      0x7f, 0x68, 0x8a, 0xad, 0x18, 0x19, 0x58, 0x20, 0x02, 0xf7, 0x7d, 0xac,
      0x9f, 0x51, 0x08, 0x92, 0x01, 0xec, 0x78, 0x9a, 0x98, 0x3c, 0xa8, 0x0d,
      0x60, 0x51, 0xcc, 0xa1, 0xda, 0x98, 0x3b, 0xb8, 0xcb, 0x8e, 0xb7, 0xe0,
      0xc7, 0x02, 0xaf, 0x0a, 0x18, 0x1a, 0x58, 0x20, 0x0b, 0x0e, 0x24, 0xdb,
      0xa7, 0x46, 0x48, 0xad, 0x2f, 0x1c, 0xbb, 0x66, 0x41, 0x46, 0xf7, 0x32,
      0x9e, 0xa4, 0xfe, 0x57, 0xe2, 0xfa, 0x5b, 0x14, 0xb6, 0x9a, 0x7f, 0x3e,
      0x3d, 0x98, 0xa0, 0x10, 0x18, 0x1b, 0x58, 0x20, 0x7f, 0x8c, 0x39, 0x1c,
      0xe7, 0xcd, 0x6d, 0xaa, 0x75, 0xeb, 0xd7, 0xb5, 0xcd, 0x32, 0xf6, 0x1c,
      0xd4, 0xf1, 0xdd, 0xe4, 0x5a, 0xba, 0xa1, 0xba, 0xdf, 0x35, 0x67, 0x1c,
      0x94, 0xa8, 0x18, 0x40, 0x18, 0x1c, 0x58, 0x20, 0x45, 0x52, 0x2a, 0xab,
      0xfe, 0x53, 0x5e, 0x05, 0x07, 0x16, 0x81, 0x0f, 0x0a, 0x72, 0x6b, 0x79,
      0x00, 0xc5, 0xf7, 0xc2, 0x9d, 0xeb, 0x16, 0x0b, 0x7e, 0x1b, 0x38, 0xe4,
      0x42, 0x7f, 0x10, 0x2c, 0x18, 0x1d, 0x58, 0x20, 0x96, 0x61, 0x0d, 0x45,
      0x9e, 0x4b, 0xf5, 0x69, 0xb6, 0x73, 0x62, 0xcf, 0x08, 0x55, 0xff, 0xb3,
      0xdd, 0x76, 0xc7, 0x0b, 0x62, 0x49, 0x48, 0xce, 0x63, 0xd5, 0x93, 0x90,
      0xa2, 0x62, 0xba, 0x27, 0x18, 0x1e, 0x58, 0x20, 0xce, 0x79, 0xa1, 0xc5,
      0x83, 0x75, 0x95, 0x5e, 0x00, 0x14, 0xdc, 0x4f, 0x2b, 0xdb, 0xc3, 0x3e,
      0x26, 0x79, 0x6c, 0x0c, 0x5b, 0x32, 0xda, 0x23, 0x40, 0xa4, 0x3b, 0x2d,
      0xc9, 0x81, 0x34, 0xf0, 0x18, 0x1f, 0x58, 0x20, 0x78, 0x71, 0x6e, 0x52,
      0x0b, 0xcf, 0xc9, 0x77, 0x59, 0x74, 0x88, 0x7d, 0x41, 0x87, 0xe2, 0x0c,
      0xef, 0xb9, 0x79, 0xe0, 0xa7, 0xf5, 0xd1, 0xc4, 0xdf, 0x11, 0xe7, 0xdd,
      0x9c, 0x21, 0xed, 0x38, 0x18, 0x20, 0x58, 0x20, 0x5f, 0xc6, 0xed, 0x71,
      0x46, 0xa0, 0xf4, 0xe3, 0x82, 0x97, 0x59, 0x1a, 0x58, 0x86, 0xba, 0x91,
      0x62, 0xf8, 0x65, 0xcc, 0xf7, 0xa6, 0x0d, 0x7e, 0xb7, 0xdc, 0x0b, 0x97,
      0xb3, 0xed, 0xa2, 0xdf, 0x18, 0x21, 0x58, 0x20, 0x22, 0x1d, 0xec, 0x99,
      0xf8, 0xe5, 0x3a, 0xff, 0xc1, 0xac, 0x36, 0x98, 0x0e, 0xdd, 0x30, 0xd2,
      0x45, 0xa0, 0x7d, 0x9e, 0x8a, 0x8f, 0xfe, 0xf8, 0x4e, 0xc4, 0x41, 0x53,
      0x1d, 0x62, 0xed, 0x56, 0x18, 0x22, 0x58, 0x20, 0x2b, 0x1a, 0xcd, 0xda,
      0xf5, 0xa5, 0x35, 0xc9, 0x43, 0xf9, 0x77, 0x0d, 0x18, 0x0e, 0x29, 0x6e,
      0x1e, 0xa7, 0x48, 0xbd, 0xd3, 0x5c, 0xc8, 0xef, 0xfe, 0x6d, 0xa6, 0x69,
      0x66, 0x9f, 0x01, 0x4d, 0x18, 0x23, 0x58, 0x20, 0x77, 0x6c, 0xc8, 0x79,
      0x55, 0x8d, 0xf5, 0x5b, 0x38, 0xc7, 0x8e, 0xbe, 0xdf, 0xbf, 0x80, 0x8c,
      0xfe, 0x42, 0x1f, 0x24, 0xeb, 0xd6, 0x16, 0x1a, 0x8b, 0xbf, 0xf4, 0xc9,
      0x54, 0x70, 0x82, 0x9a, 0x18, 0x24, 0x58, 0x20, 0x4b, 0x7e, 0x0f, 0x51,
      0x63, 0xbe, 0xa3, 0x76, 0xcf, 0x47, 0x76, 0xbd, 0x52, 0xa7, 0x76, 0x57,
      0x28, 0xeb, 0xae, 0x1d, 0x17, 0x50, 0xc2, 0xc9, 0xc5, 0xb8, 0xa3, 0x08,
      0xb3, 0x3a, 0x4f, 0xfd, 0x18, 0x25, 0x58, 0x20, 0x0d, 0x76, 0x9d, 0x48,
      0x03, 0x55, 0x16, 0xca, 0x03, 0x59, 0x5c, 0x7f, 0xa6, 0x0d, 0xd4, 0x32,
      0xfe, 0x75, 0xe0, 0x88, 0x11, 0x10, 0x15, 0x64, 0x89, 0x66, 0x8f, 0x92,
      0xd1, 0xc0, 0x1b, 0xcb, 0x18, 0x26, 0x58, 0x20, 0x02, 0x70, 0x12, 0x7a,
      0xb6, 0x66, 0x44, 0x57, 0x53, 0xd6, 0x12, 0x52, 0xca, 0xf0, 0x5d, 0x4a,
      0x2f, 0x20, 0x77, 0x35, 0xca, 0xe9, 0x34, 0x9d, 0xdf, 0x35, 0x2c, 0x27,
      0xea, 0xe7, 0x6e, 0xe8, 0x18, 0x27, 0x58, 0x20, 0x42, 0x9a, 0x96, 0xe3,
      0x79, 0x51, 0xb1, 0xd8, 0x99, 0x98, 0x28, 0x81, 0x4a, 0xa2, 0x52, 0x68,
      0xf8, 0xeb, 0x69, 0x53, 0xce, 0x41, 0x7f, 0xa9, 0x93, 0xf8, 0xd1, 0x47,
      0xf2, 0xe0, 0xe7, 0x45, 0x18, 0x28, 0x58, 0x20, 0xf5, 0x03, 0xb9, 0x30,
      0x8e, 0x63, 0x45, 0xb9, 0x9e, 0x0f, 0x01, 0xc6, 0x73, 0xc2, 0xcf, 0x0f,
      0x84, 0x5e, 0x22, 0xfe, 0x1f, 0x77, 0x6a, 0xc8, 0xe4, 0x1e, 0xb1, 0x90,
      0x58, 0x8d, 0xaf, 0x6a, 0x18, 0x29, 0x58, 0x20, 0xae, 0x02, 0x5f, 0x21,
      0xb2, 0x08, 0x9d, 0xf9, 0x21, 0x6d, 0x6c, 0x7d, 0x65, 0x50, 0xf3, 0x36,
      0x4b, 0x42, 0x19, 0x7b, 0x1f, 0x3f, 0xe2, 0x63, 0x72, 0xdc, 0x9c, 0x0c,
      0xa7, 0xe3, 0x0c, 0x08, 0x18, 0x2a, 0x58, 0x20, 0x3e, 0xcd, 0x92, 0xc4,
      0x9c, 0x3f, 0xee, 0x18, 0x41, 0xa9, 0xa5, 0xd7, 0x6e, 0x9e, 0xd5, 0xf0,
      0xfd, 0x06, 0x22, 0x72, 0x98, 0xe7, 0x87, 0x6d, 0x25, 0xc1, 0xf6, 0x1f,
      0x79, 0xb4, 0x37, 0xc0, 0x77, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73, 0x6f,
      0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0x2e, 0x61,
      0x61, 0x6d, 0x76, 0x61, 0xa7, 0x00, 0x58, 0x20, 0xb9, 0x3d, 0x05, 0x94,
      0xaf, 0x96, 0xfe, 0x77, 0x1a, 0xc8, 0x79, 0x7f, 0x9e, 0x75, 0x76, 0xf8,
      0xd3, 0xa7, 0xd3, 0x14, 0x31, 0x7a, 0x7c, 0x6c, 0x29, 0x32, 0xbc, 0xe1,
      0xf4, 0xde, 0x4f, 0x64, 0x01, 0x58, 0x20, 0xd8, 0x36, 0x54, 0x23, 0x07,
      0x2f, 0x73, 0xbf, 0x62, 0x88, 0x46, 0x20, 0xd8, 0xb6, 0xdc, 0x27, 0xb2,
      0x27, 0xe6, 0x56, 0xc7, 0xd6, 0xb2, 0x5e, 0xb0, 0xd6, 0xc8, 0xd5, 0xe2,
      0x5c, 0x4a, 0x44, 0x02, 0x58, 0x20, 0xc1, 0xcb, 0x4f, 0x9a, 0xa4, 0x1b,
      0x2f, 0x9b, 0xa1, 0x7a, 0x33, 0xaf, 0x86, 0xac, 0xbc, 0x47, 0x16, 0xf3,
      0x8e, 0xf8, 0x91, 0x9f, 0x19, 0xd9, 0x8a, 0xfc, 0x33, 0x7c, 0x41, 0x5f,
      0xe6, 0x1d, 0x03, 0x58, 0x20, 0x45, 0xdb, 0x9c, 0x8f, 0x0a, 0xe3, 0x43,
      0x09, 0x0f, 0x2a, 0x39, 0x07, 0x9e, 0x89, 0xa4, 0x6a, 0xfb, 0xb4, 0x87,
      0x1b, 0xc8, 0xc7, 0x70, 0x44, 0x62, 0xc0, 0xf4, 0x35, 0x48, 0x27, 0x74,
      0x80, 0x04, 0x58, 0x20, 0x37, 0x0f, 0x26, 0x20, 0x67, 0x83, 0x1c, 0x85,
      0x1d, 0x77, 0x84, 0xb1, 0x39, 0x5e, 0x73, 0x9e, 0x8f, 0xa0, 0x25, 0xe1,
      0x8b, 0x92, 0x37, 0xdf, 0xa7, 0x10, 0x05, 0x87, 0x6e, 0xb4, 0x8b, 0x06,
      0x05, 0x58, 0x20, 0x8c, 0xb6, 0xa4, 0x05, 0x06, 0xf7, 0xb1, 0x01, 0x20,
      0x32, 0x46, 0x95, 0xef, 0x20, 0x15, 0xad, 0x45, 0xbe, 0x93, 0x33, 0x14,
      0xb7, 0x5e, 0xa2, 0xb2, 0x50, 0xd6, 0x8b, 0xa9, 0x73, 0xd0, 0x09, 0x06,
      0x58, 0x20, 0xe9, 0x00, 0x40, 0x02, 0xf9, 0x59, 0xc8, 0xe1, 0x80, 0x2d,
      0x9b, 0xb2, 0x95, 0x6e, 0xc0, 0x07, 0x58, 0x18, 0x6e, 0xd1, 0x02, 0x61,
      0x94, 0xa1, 0x3c, 0xc6, 0x79, 0x56, 0x34, 0x4a, 0x6c, 0xd5, 0x6d, 0x64,
      0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x49, 0x6e, 0x66, 0x6f,
      0xa1, 0x69, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0xa4,
      0x01, 0x02, 0x20, 0x01, 0x21, 0x58, 0x20, 0xd8, 0x03, 0x40, 0x9d, 0x10,
      0x60, 0x75, 0xeb, 0x45, 0x83, 0xe1, 0xca, 0x94, 0xad, 0x54, 0x2b, 0x57,
      0x79, 0xf1, 0xe8, 0x64, 0xa8, 0xd2, 0x46, 0x81, 0xa1, 0xa6, 0x2e, 0x3b,
      0x50, 0xdb, 0xd3, 0x22, 0x58, 0x20, 0xd6, 0x66, 0x3f, 0x27, 0x76, 0x66,
      0x68, 0x02, 0x6a, 0x25, 0x0c, 0x9e, 0xd2, 0xcb, 0xbb, 0xdd, 0xd7, 0x57,
      0xe3, 0x03, 0x8a, 0xea, 0xf1, 0x07, 0x8f, 0xda, 0xb5, 0xd2, 0xb1, 0x82,
      0x6b, 0xa0, 0x6c, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x69, 0x74, 0x79, 0x49,
      0x6e, 0x66, 0x6f, 0xa3, 0x66, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xc0,
      0x74, 0x32, 0x30, 0x32, 0x34, 0x2d, 0x30, 0x35, 0x2d, 0x30, 0x33, 0x54,
      0x30, 0x37, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x5a, 0x69, 0x76, 0x61,
      0x6c, 0x69, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0xc0, 0x74, 0x32, 0x30, 0x32,
      0x34, 0x2d, 0x30, 0x35, 0x2d, 0x30, 0x33, 0x54, 0x30, 0x37, 0x3a, 0x30,
      0x30, 0x3a, 0x30, 0x30, 0x5a, 0x6a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55,
      0x6e, 0x74, 0x69, 0x6c, 0xc0, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2d, 0x30,
      0x35, 0x2d, 0x30, 0x33, 0x54, 0x30, 0x37, 0x3a, 0x30, 0x30, 0x3a, 0x30,
      0x30, 0x5a, 0x67, 0x64, 0x6f, 0x63, 0x54, 0x79, 0x70, 0x65, 0x75, 0x6f,
      0x72, 0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33,
      0x2e, 0x35, 0x2e, 0x31, 0x2e, 0x6d, 0x44, 0x4c, 0x58, 0x40, 0xfb, 0x37,
      0x77, 0xd9, 0xbf, 0x2d, 0x25, 0x26, 0xe3, 0x0d, 0x8d, 0xf4, 0x59, 0xe0,
      0x9e, 0x70, 0x48, 0x28, 0x40, 0x8d, 0x60, 0xa7, 0xc1, 0x3c, 0x14, 0xe9,
      0x4f, 0xe4, 0x3d, 0xf3, 0x5e, 0xe2, 0xaf, 0x86, 0xe9, 0x60, 0x87, 0xf0,
      0x40, 0x1b, 0xd0, 0xa7, 0x2f, 0x6e, 0x45, 0x16, 0xdf, 0xa9, 0x9d, 0xb1,
      0xf1, 0xb7, 0x2d, 0xa5, 0xb7, 0x39, 0x4f, 0x86, 0x26, 0xe1, 0x98, 0x59,
      0x27, 0xaf, 0x6c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x69, 0x67,
      0x6e, 0x65, 0x64, 0xa2, 0x6a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x70, 0x61,
      0x63, 0x65, 0x73, 0xd8, 0x18, 0x41, 0xa0, 0x6a, 0x64, 0x65, 0x76, 0x69,
      0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0xa1, 0x6f, 0x64, 0x65, 0x76, 0x69,
      0x63, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x84,
      0x43, 0xa1, 0x01, 0x26, 0xa0, 0xf6, 0x58, 0x40, 0xef, 0xde, 0xb6, 0xa0,
      0x91, 0x85, 0x5f, 0x91, 0x1c, 0x5f, 0x94, 0x68, 0xd0, 0x4f, 0xe3, 0x9b,
      0xee, 0xff, 0x3c, 0x9e, 0xb4, 0x32, 0x4a, 0xc4, 0x76, 0x9a, 0x5e, 0x81,
      0x2d, 0x52, 0x45, 0x3b, 0xc6, 0x48, 0xf8, 0xc5, 0xe0, 0x3d, 0x12, 0x67,
      0x7d, 0xf4, 0xf9, 0x6b, 0x9e, 0x56, 0xb0, 0xc5, 0x0f, 0x73, 0x7a, 0x02,
      0xf7, 0x6b, 0x6f, 0x25, 0xe2, 0x50, 0x28, 0x34, 0xa5, 0x1d, 0x31, 0x47,
      0x66, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x00}},

    // Sprind-Funke mdoc example that includes family_name,
    // birth_date, issue_date, height, and age_over_18.
    // To test these examples, change the kAgeShow[] data structure in
    // mdoc_zk.cc to include one of the following opened attributes:
    // {(uint8_t *)"family_name", 11, (uint8_t *)"Mustermann", 10},
    // {(uint8_t *)"height", 6, (uint8_t *)"\x18\xaf", 2},
    // {(uint8_t *)"birth_date", 10, (uint8_t *)"\xD9\x03\xEC\x6A"
    //        "1971-09-01", 14},
    // {(uint8_t *)"issue_date", 10, (uint8_t *)"\xD9\x03\xEC\x6A"
    //        "2024-03-15", 14},
    // Note that the D903EC6A prefix specifies the cbor full-date encoding for
    // the birth_date and issue_date. The spec suggests that the issue_date can
    // also be formatted as a tdate, which would have a different prefix.
    // The 0x18af formatting for the height attribute uses the uint formatting
    // and corresponds to the value 175.
    {
        StaticString("0xdc1c1f55cff4cd5c76cf4169278f7217667f86ee81d8669b63f2e19"
                     "bc12a0c9f"),
        StaticString("0x12355dd0385fed3bc33bedc9781b9aad47b33e4c24704b8d14288b1"
                     "b3cb45c28"),
        {0x83, 0xf6, 0xf6, 0x84, 0x71, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72,
         0x48, 0x61, 0x6e, 0x64, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x31, 0x58, 0x20,
         0xf9, 0x3e, 0xba, 0xc4, 0xce, 0x4d, 0x99, 0x01, 0xb9, 0xae, 0xa4, 0x72,
         0x14, 0x5a, 0xe5, 0x42, 0x1f, 0x8f, 0xbe, 0xcb, 0xe5, 0xf0, 0x38, 0x96,
         0x83, 0xf5, 0x9f, 0x08, 0xfc, 0xf9, 0x0e, 0x45, 0x58, 0x33, 0xa3, 0x63,
         0x63, 0x61, 0x74, 0x01, 0x64, 0x74, 0x79, 0x70, 0x65, 0x01, 0x67, 0x64,
         0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0xa1, 0x67, 0x62, 0x61, 0x73, 0x65,
         0x55, 0x72, 0x6c, 0x75, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c,
         0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x3a, 0x38, 0x30, 0x38,
         0x30, 0x58, 0x20, 0x3c, 0x79, 0x91, 0x4b, 0x7f, 0x81, 0xa1, 0xc2, 0x55,
         0x8f, 0xc8, 0x16, 0x19, 0xdd, 0x4a, 0x07, 0x4d, 0x32, 0x14, 0x3e, 0x6c,
         0xf6, 0x89, 0x5f, 0xe4, 0x7d, 0xa1, 0x56, 0xd1, 0xc5, 0xb0, 0xae},
        143,
        (uint8_t *)"2024-10-01T09:00:00Z",
        kMDL,
        3173,
        {0xa3, 0x67, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e,
         0x30, 0x69, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x81,
         0xa3, 0x67, 0x64, 0x6f, 0x63, 0x54, 0x79, 0x70, 0x65, 0x75, 0x6f, 0x72,
         0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
         0x35, 0x2e, 0x31, 0x2e, 0x6d, 0x44, 0x4c, 0x6c, 0x69, 0x73, 0x73, 0x75,
         0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xa2, 0x6a, 0x6e, 0x61,
         0x6d, 0x65, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0xa1, 0x71, 0x6f, 0x72,
         0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
         0x35, 0x2e, 0x31, 0x85, 0xd8, 0x18, 0x58, 0x5a, 0xa4, 0x68, 0x64, 0x69,
         0x67, 0x65, 0x73, 0x74, 0x49, 0x44, 0x18, 0x1a, 0x66, 0x72, 0x61, 0x6e,
         0x64, 0x6f, 0x6d, 0x50, 0x72, 0xc7, 0xaf, 0x26, 0x49, 0xd0, 0x5a, 0x38,
         0xb0, 0x2e, 0x7e, 0x1c, 0xc7, 0xf9, 0xd0, 0x46, 0x71, 0x65, 0x6c, 0x65,
         0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
         0x65, 0x72, 0x6b, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x5f, 0x6e, 0x61,
         0x6d, 0x65, 0x6c, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61,
         0x6c, 0x75, 0x65, 0x6a, 0x4d, 0x75, 0x73, 0x74, 0x65, 0x72, 0x6d, 0x61,
         0x6e, 0x6e, 0xd8, 0x18, 0x58, 0x5b, 0xa4, 0x68, 0x64, 0x69, 0x67, 0x65,
         0x73, 0x74, 0x49, 0x44, 0x0a, 0x66, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d,
         0x50, 0xfc, 0x8a, 0x8f, 0x5b, 0xaf, 0xb4, 0x0c, 0xb3, 0x21, 0x77, 0x6d,
         0xbf, 0x5f, 0xbe, 0x18, 0x81, 0x71, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
         0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x6a,
         0x62, 0x69, 0x72, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x6c, 0x65,
         0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0xd9,
         0x03, 0xec, 0x6a, 0x31, 0x39, 0x37, 0x31, 0x2d, 0x30, 0x39, 0x2d, 0x30,
         0x31, 0xd8, 0x18, 0x58, 0x5b, 0xa4, 0x68, 0x64, 0x69, 0x67, 0x65, 0x73,
         0x74, 0x49, 0x44, 0x14, 0x66, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50,
         0x59, 0xcd, 0xdd, 0x91, 0xd7, 0x53, 0xed, 0x98, 0xff, 0x55, 0x5d, 0x2f,
         0x97, 0x27, 0x45, 0x3b, 0x71, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
         0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x6a, 0x69,
         0x73, 0x73, 0x75, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x6c,
         0x65, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0xd9, 0x03,
         0xec, 0x6a, 0x32, 0x30, 0x32, 0x34, 0x2d, 0x30, 0x33, 0x2d, 0x31, 0x35,
         0xd8, 0x18, 0x58, 0x4b, 0xa4, 0x68, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74,
         0x49, 0x44, 0x0f, 0x66, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0xcc,
         0xdc, 0x59, 0x8c, 0xa9, 0xaf, 0xee, 0x95, 0xfd, 0x57, 0x68, 0x3e, 0xb1,
         0x9a, 0xff, 0x33, 0x71, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x49,
         0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x66, 0x68, 0x65,
         0x69, 0x67, 0x68, 0x74, 0x6c, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
         0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xaf, 0xd8, 0x18, 0x58, 0x4f, 0xa4,
         0x68, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x49, 0x44, 0x15, 0x66, 0x72,
         0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0xac, 0x50, 0xf6, 0x51, 0xbc, 0x04,
         0xf0, 0x01, 0xd0, 0x21, 0x63, 0x84, 0x7c, 0x9f, 0x1c, 0xc9, 0x71, 0x65,
         0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69,
         0x66, 0x69, 0x65, 0x72, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6f, 0x76, 0x65,
         0x72, 0x5f, 0x31, 0x38, 0x6c, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
         0x56, 0x61, 0x6c, 0x75, 0x65, 0xf5, 0x6a, 0x69, 0x73, 0x73, 0x75, 0x65,
         0x72, 0x41, 0x75, 0x74, 0x68, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa1, 0x18,
         0x21, 0x59, 0x02, 0x83, 0x30, 0x82, 0x02, 0x7f, 0x30, 0x82, 0x02, 0x25,
         0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x35, 0x00, 0xba, 0x9b, 0xef,
         0xd9, 0x1c, 0xb9, 0xda, 0x79, 0x05, 0xff, 0xef, 0x18, 0xbe, 0x8a, 0x30,
         0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30,
         0x39, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
         0x55, 0x54, 0x31, 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
         0x21, 0x4f, 0x57, 0x46, 0x20, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
         0x79, 0x20, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
         0x20, 0x54, 0x45, 0x53, 0x54, 0x20, 0x49, 0x41, 0x43, 0x41, 0x30, 0x1e,
         0x17, 0x0d, 0x32, 0x34, 0x30, 0x39, 0x30, 0x32, 0x31, 0x37, 0x32, 0x31,
         0x31, 0x33, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x39, 0x30, 0x32, 0x31,
         0x37, 0x32, 0x31, 0x31, 0x33, 0x5a, 0x30, 0x37, 0x31, 0x28, 0x30, 0x26,
         0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1f, 0x4f, 0x57, 0x46, 0x20, 0x49,
         0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x43, 0x72, 0x65, 0x64,
         0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x54, 0x45, 0x53, 0x54, 0x20,
         0x44, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
         0x02, 0x55, 0x54, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48,
         0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03,
         0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xdc, 0x1c, 0x1f, 0x55, 0xcf, 0xf4,
         0xcd, 0x5c, 0x76, 0xcf, 0x41, 0x69, 0x27, 0x8f, 0x72, 0x17, 0x66, 0x7f,
         0x86, 0xee, 0x81, 0xd8, 0x66, 0x9b, 0x63, 0xf2, 0xe1, 0x9b, 0xc1, 0x2a,
         0x0c, 0x9f, 0x12, 0x35, 0x5d, 0xd0, 0x38, 0x5f, 0xed, 0x3b, 0xc3, 0x3b,
         0xed, 0xc9, 0x78, 0x1b, 0x9a, 0xad, 0x47, 0xb3, 0x3e, 0x4c, 0x24, 0x70,
         0x4b, 0x8d, 0x14, 0x28, 0x8b, 0x1b, 0x3c, 0xb4, 0x5c, 0x28, 0xa3, 0x82,
         0x01, 0x0f, 0x30, 0x82, 0x01, 0x0b, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
         0x0e, 0x04, 0x16, 0x04, 0x14, 0x13, 0x09, 0xce, 0x86, 0x0b, 0x9d, 0xff,
         0xbc, 0x85, 0x99, 0x28, 0x3a, 0x1d, 0xd9, 0xc1, 0xf3, 0x55, 0x6a, 0x63,
         0xf1, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
         0x80, 0x14, 0x3c, 0xc0, 0x46, 0x99, 0x0e, 0x93, 0x42, 0x85, 0x46, 0x6d,
         0xf6, 0xc6, 0xe0, 0x9a, 0xe3, 0xe3, 0x68, 0xdf, 0x2b, 0x0e, 0x30, 0x0e,
         0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
         0x07, 0x80, 0x30, 0x15, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff,
         0x04, 0x0b, 0x30, 0x09, 0x06, 0x07, 0x28, 0x81, 0x8c, 0x5d, 0x05, 0x01,
         0x02, 0x30, 0x54, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x4d, 0x04, 0x4b,
         0x30, 0x49, 0x30, 0x47, 0xa0, 0x45, 0xa0, 0x43, 0x86, 0x41, 0x68, 0x74,
         0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
         0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x77, 0x61, 0x6c,
         0x6c, 0x65, 0x74, 0x2d, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69,
         0x6f, 0x6e, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x69, 0x64, 0x65, 0x6e,
         0x74, 0x69, 0x74, 0x79, 0x2d, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
         0x69, 0x61, 0x6c, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, 0x45,
         0x04, 0x43, 0x86, 0x41, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
         0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f,
         0x70, 0x65, 0x6e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2d, 0x66, 0x6f,
         0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6c, 0x61, 0x62,
         0x73, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2d, 0x63,
         0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x30, 0x0a, 0x06,
         0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00,
         0x30, 0x45, 0x02, 0x21, 0x00, 0xcc, 0x61, 0x2d, 0xb2, 0xda, 0x22, 0x60,
         0x9b, 0xf0, 0xa9, 0xeb, 0xea, 0x39, 0xa9, 0x05, 0x74, 0xfa, 0xc6, 0xd1,
         0xb0, 0xed, 0x8b, 0x8b, 0xe0, 0x45, 0x27, 0x3e, 0xc7, 0x27, 0x6f, 0x64,
         0x0c, 0x02, 0x20, 0x7f, 0x21, 0x44, 0x30, 0x13, 0x45, 0x54, 0x40, 0x93,
         0x53, 0xb4, 0xb7, 0x4c, 0x4b, 0x64, 0x52, 0x64, 0x15, 0x81, 0x6d, 0x2f,
         0xcb, 0x43, 0x29, 0x57, 0x0c, 0x62, 0xd5, 0x56, 0x27, 0x2b, 0x54, 0x59,
         0x06, 0xdb, 0xd8, 0x18, 0x59, 0x06, 0xd6, 0xa6, 0x67, 0x76, 0x65, 0x72,
         0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e, 0x30, 0x6f, 0x64, 0x69, 0x67,
         0x65, 0x73, 0x74, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d,
         0x67, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x67, 0x64, 0x6f, 0x63,
         0x54, 0x79, 0x70, 0x65, 0x75, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73, 0x6f,
         0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0x2e, 0x6d,
         0x44, 0x4c, 0x6c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x69, 0x67, 0x65,
         0x73, 0x74, 0x73, 0xa2, 0x71, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73, 0x6f,
         0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0xb8, 0x22,
         0x18, 0x1a, 0x58, 0x20, 0x2c, 0xb7, 0x3e, 0x3e, 0xc2, 0x84, 0xc0, 0x76,
         0x43, 0xcb, 0x0c, 0xf5, 0xdf, 0x2d, 0xc0, 0xac, 0x83, 0xf8, 0xb2, 0x36,
         0xb3, 0xa9, 0xb7, 0x32, 0xb6, 0x8e, 0x4b, 0x3b, 0x64, 0x46, 0xf5, 0x79,
         0x18, 0x1c, 0x58, 0x20, 0x9b, 0xbe, 0x80, 0x85, 0x23, 0x6e, 0xd0, 0x36,
         0x89, 0x0f, 0x9b, 0x03, 0xf0, 0x18, 0x53, 0x1c, 0x33, 0xb2, 0x4b, 0x34,
         0xb2, 0x95, 0x2d, 0xe6, 0xdb, 0x79, 0x1f, 0x39, 0x02, 0xc4, 0xa6, 0x6d,
         0x0a, 0x58, 0x20, 0x5b, 0xde, 0xa4, 0x3a, 0x31, 0xd1, 0x78, 0xc3, 0xc7,
         0x2f, 0xec, 0x44, 0xc7, 0x1b, 0xe5, 0x15, 0xa2, 0x68, 0x96, 0x27, 0xbe,
         0x2d, 0x3e, 0x71, 0x5c, 0x3b, 0xba, 0x4e, 0x25, 0x78, 0x53, 0x34, 0x14,
         0x58, 0x20, 0x55, 0xe0, 0xe0, 0x5e, 0xc0, 0xd6, 0xe2, 0x59, 0x20, 0xf4,
         0x6e, 0x64, 0x28, 0x6c, 0xa9, 0x81, 0xf4, 0xfc, 0xba, 0xe9, 0xa0, 0xbc,
         0xbd, 0x8b, 0x89, 0x3a, 0x22, 0x5d, 0x94, 0xa6, 0xc6, 0x94, 0x16, 0x58,
         0x20, 0xc8, 0xf2, 0xef, 0x54, 0xbf, 0xa6, 0xb8, 0x19, 0x67, 0xf4, 0x78,
         0xb5, 0xa7, 0xa9, 0xee, 0x36, 0x64, 0xf6, 0x00, 0xac, 0x8d, 0x28, 0x9f,
         0xef, 0xd6, 0xa8, 0x03, 0xd1, 0x10, 0x7a, 0xa6, 0xb0, 0x18, 0x1f, 0x58,
         0x20, 0x6a, 0x93, 0x6b, 0xd3, 0x48, 0x23, 0xee, 0x83, 0x20, 0x9d, 0x76,
         0x4d, 0x1e, 0x5d, 0x38, 0xa7, 0x0c, 0x0d, 0xf2, 0x08, 0x93, 0x71, 0xcb,
         0xb9, 0x84, 0x4c, 0xff, 0xf9, 0xb5, 0xc5, 0xc8, 0xef, 0x18, 0x27, 0x58,
         0x20, 0x1a, 0x40, 0x41, 0x2d, 0xf1, 0x81, 0x55, 0x2d, 0xe9, 0xb5, 0x41,
         0x74, 0x46, 0x66, 0x04, 0x9a, 0x86, 0x9f, 0xb2, 0x7e, 0x2d, 0x58, 0x86,
         0xf7, 0xa9, 0x97, 0x28, 0xeb, 0xb8, 0xb9, 0x44, 0x30, 0x07, 0x58, 0x20,
         0x38, 0xb1, 0x69, 0x90, 0xb9, 0xfc, 0x76, 0xf2, 0x3a, 0x85, 0x16, 0x69,
         0x0a, 0x28, 0xa8, 0x22, 0x79, 0xd2, 0xcb, 0xd9, 0x6c, 0xdf, 0xd8, 0x89,
         0xe4, 0x2b, 0xac, 0xbf, 0x06, 0xb9, 0xf0, 0x52, 0x18, 0x25, 0x58, 0x20,
         0xe7, 0x8b, 0x03, 0x63, 0x13, 0x0b, 0x2c, 0x0c, 0xd8, 0x87, 0x03, 0x1d,
         0x42, 0x0b, 0xc9, 0xd3, 0x86, 0x87, 0x05, 0x35, 0xfb, 0x16, 0x73, 0xcc,
         0xd8, 0x6a, 0xcb, 0x83, 0xde, 0x2a, 0x10, 0x7c, 0x18, 0x21, 0x58, 0x20,
         0x89, 0x90, 0xf0, 0xdc, 0x1a, 0xde, 0xd7, 0xa4, 0x61, 0x34, 0x81, 0x2c,
         0x44, 0x0f, 0xc0, 0x8b, 0xeb, 0xaa, 0xfb, 0xf4, 0x72, 0xbd, 0x29, 0x8d,
         0xf2, 0x8f, 0x1c, 0x65, 0xbd, 0xd0, 0x07, 0xdf, 0x08, 0x58, 0x20, 0x75,
         0x1e, 0x3b, 0x24, 0xef, 0xb3, 0x2f, 0x74, 0x08, 0x11, 0x2b, 0x12, 0xe8,
         0x86, 0xec, 0x4c, 0xf6, 0x17, 0x61, 0x56, 0x2f, 0x5a, 0xbd, 0x5a, 0xe5,
         0x71, 0xc7, 0x30, 0x72, 0x30, 0x6c, 0xd9, 0x03, 0x58, 0x20, 0x52, 0x09,
         0x57, 0x17, 0x6a, 0x82, 0x2a, 0x51, 0x94, 0x47, 0xd6, 0x15, 0x23, 0x4b,
         0xcd, 0x6b, 0x97, 0x2a, 0x71, 0x54, 0xf2, 0xa4, 0x69, 0x99, 0x2b, 0x2e,
         0xc1, 0x86, 0x29, 0x18, 0x19, 0x58, 0x0f, 0x58, 0x20, 0x7f, 0x49, 0x2e,
         0xcd, 0xad, 0xe9, 0xb0, 0x15, 0xf1, 0x9e, 0x96, 0x56, 0xb7, 0xcb, 0x4f,
         0x27, 0xd9, 0x68, 0x40, 0xee, 0xd5, 0x34, 0x26, 0x64, 0xdc, 0xd2, 0x23,
         0xd1, 0x56, 0x41, 0xa8, 0x1b, 0x13, 0x58, 0x20, 0x9d, 0x2c, 0xf0, 0x53,
         0x97, 0x9a, 0x21, 0x62, 0x2d, 0xb9, 0x13, 0x95, 0xf7, 0x8a, 0xaf, 0x5b,
         0x9c, 0x92, 0x39, 0x16, 0x53, 0xd8, 0xc5, 0xbb, 0x42, 0x68, 0xa6, 0x40,
         0x83, 0x96, 0xfe, 0x28, 0x04, 0x58, 0x20, 0xf7, 0x97, 0xdf, 0xe4, 0x0f,
         0x5d, 0x9a, 0x61, 0x6c, 0x9d, 0x26, 0xf3, 0x74, 0x22, 0x51, 0x56, 0xa0,
         0x9e, 0x61, 0xae, 0xa4, 0xa4, 0x38, 0x60, 0x68, 0x18, 0xd4, 0xbd, 0x2c,
         0xe6, 0x18, 0xb0, 0x0c, 0x58, 0x20, 0xe5, 0x95, 0x37, 0xec, 0x38, 0xe7,
         0xa7, 0x8f, 0xe9, 0xe8, 0xc0, 0xe8, 0xa8, 0x18, 0x81, 0xe7, 0xc1, 0xe9,
         0x2f, 0x11, 0x17, 0xb6, 0x29, 0x7a, 0xcf, 0xf1, 0x7c, 0x8e, 0x74, 0xd0,
         0xad, 0x12, 0x09, 0x58, 0x20, 0x6f, 0x7f, 0xb1, 0xdf, 0x35, 0x2f, 0x23,
         0x2f, 0x25, 0x9a, 0xc0, 0xa4, 0xee, 0x39, 0x76, 0x09, 0x3a, 0x32, 0x4f,
         0xd1, 0xb1, 0xa1, 0xf3, 0x15, 0xb2, 0x48, 0x1d, 0x40, 0xa0, 0x69, 0x7c,
         0xc5, 0x18, 0x18, 0x58, 0x20, 0x70, 0x58, 0x58, 0x66, 0xab, 0xf1, 0xc5,
         0xfb, 0x60, 0xd5, 0xf4, 0xb0, 0x9a, 0xa8, 0xab, 0xb9, 0xf9, 0x52, 0x2a,
         0x14, 0x2d, 0xc3, 0x54, 0xa1, 0x1c, 0xbe, 0xb8, 0x58, 0xf6, 0x1c, 0xa1,
         0x40, 0x06, 0x58, 0x20, 0xb7, 0x60, 0x27, 0x85, 0xef, 0x55, 0x31, 0x9c,
         0x6e, 0xf1, 0xd3, 0xea, 0x6f, 0x36, 0x1d, 0xa9, 0xe1, 0xf0, 0x04, 0x50,
         0x13, 0xcd, 0x2f, 0x74, 0xa1, 0xd8, 0x24, 0x7d, 0x1a, 0x75, 0x86, 0xbe,
         0x12, 0x58, 0x20, 0xc1, 0x9a, 0xdf, 0x46, 0x4a, 0x9b, 0x22, 0xc5, 0x79,
         0xc0, 0x56, 0xbb, 0x0e, 0x45, 0x98, 0xb8, 0x90, 0xa0, 0xbb, 0x16, 0x83,
         0xb7, 0x29, 0x06, 0x7f, 0xa8, 0x2c, 0x5e, 0x6a, 0x10, 0x3d, 0x2d, 0x18,
         0x1b, 0x58, 0x20, 0x94, 0x97, 0xfa, 0xb1, 0xcb, 0xa4, 0x6c, 0xe4, 0x10,
         0x90, 0x9f, 0x5d, 0xb0, 0xea, 0xda, 0x49, 0xc5, 0xf0, 0x19, 0x5e, 0xcb,
         0x03, 0x22, 0x0d, 0xe9, 0xba, 0x88, 0xc9, 0x61, 0xc4, 0xbb, 0xf8, 0x00,
         0x58, 0x20, 0x5b, 0x22, 0x5a, 0xa1, 0xc6, 0x31, 0x6a, 0xcf, 0xed, 0x2b,
         0xe6, 0x70, 0x15, 0xed, 0x71, 0xc2, 0xda, 0x4d, 0x87, 0x3f, 0x01, 0xa2,
         0xfd, 0xd4, 0xd6, 0x2f, 0xb3, 0x01, 0x23, 0x38, 0x6d, 0xc8, 0x0e, 0x58,
         0x20, 0xcf, 0x32, 0x74, 0x8e, 0x98, 0xfb, 0x6b, 0x9d, 0xee, 0x3d, 0x48,
         0x42, 0x1f, 0xb5, 0xf6, 0xb4, 0x50, 0x24, 0xae, 0x46, 0x87, 0x89, 0x25,
         0x79, 0xaa, 0x05, 0xe3, 0x10, 0x24, 0x60, 0x21, 0x80, 0x15, 0x58, 0x20,
         0x71, 0xcd, 0x46, 0x7c, 0x9c, 0x1c, 0x7a, 0x0c, 0x88, 0x95, 0x06, 0xe9,
         0x7e, 0x13, 0x35, 0xe0, 0xff, 0xb8, 0x1f, 0xd1, 0x9d, 0x8d, 0x5c, 0x84,
         0x8d, 0x74, 0xd7, 0x02, 0x31, 0xab, 0xfa, 0x82, 0x18, 0x20, 0x58, 0x20,
         0x88, 0x22, 0x51, 0x86, 0xc9, 0x41, 0xc8, 0x05, 0xea, 0xff, 0x63, 0x9d,
         0xa5, 0xaf, 0x04, 0xaa, 0xf0, 0xe9, 0xad, 0x7c, 0xb4, 0x84, 0xb1, 0x66,
         0x66, 0x94, 0x32, 0xed, 0x65, 0xf6, 0x3d, 0x52, 0x18, 0x26, 0x58, 0x20,
         0xc6, 0xba, 0x1f, 0x99, 0x66, 0xd5, 0x25, 0x51, 0x5d, 0xc9, 0x0c, 0x48,
         0x3f, 0x93, 0x25, 0xfe, 0x3d, 0x8d, 0x08, 0x3e, 0x2c, 0x58, 0xb6, 0x85,
         0x97, 0x2b, 0x30, 0x8b, 0x4d, 0xa4, 0x3e, 0x31, 0x0d, 0x58, 0x20, 0x33,
         0xb6, 0xfc, 0x44, 0xed, 0x68, 0x73, 0x54, 0x37, 0x11, 0xf5, 0xe7, 0x97,
         0x9d, 0xef, 0xe9, 0xa7, 0x1a, 0x1b, 0x4a, 0x25, 0x4f, 0x91, 0x64, 0xf4,
         0xfc, 0x37, 0xff, 0xb8, 0x00, 0x48, 0x50, 0x18, 0x1d, 0x58, 0x20, 0x3e,
         0x1f, 0xfd, 0x36, 0x5a, 0x73, 0xb6, 0x9e, 0x8e, 0xe3, 0x7e, 0xd9, 0x1a,
         0xc2, 0x7f, 0x79, 0xa1, 0x37, 0x00, 0xa7, 0xbc, 0x2e, 0xfe, 0xc0, 0xad,
         0x0c, 0xce, 0xd1, 0xde, 0xb5, 0xbe, 0xbf, 0x18, 0x1e, 0x58, 0x20, 0x40,
         0x89, 0xa1, 0x71, 0x6a, 0x4a, 0xd5, 0xb9, 0xd0, 0xf6, 0xd5, 0x18, 0xd3,
         0x85, 0xd5, 0x2f, 0x77, 0xa2, 0x0d, 0x50, 0x46, 0x16, 0xf9, 0xc5, 0x88,
         0xbd, 0x60, 0xc6, 0x14, 0xcd, 0x24, 0x8a, 0x11, 0x58, 0x20, 0x82, 0x1e,
         0xa8, 0x0b, 0x1b, 0x53, 0x3b, 0xf3, 0x2c, 0x6c, 0x0f, 0xce, 0xdf, 0x78,
         0x9d, 0x92, 0xac, 0xe2, 0x78, 0xc8, 0x68, 0x23, 0x06, 0x70, 0xe3, 0x41,
         0xf9, 0x51, 0xfa, 0x15, 0x0b, 0xaa, 0x01, 0x58, 0x20, 0xa5, 0x8a, 0x7b,
         0xe3, 0x89, 0x6c, 0x72, 0xb3, 0xce, 0x94, 0xe2, 0xc8, 0xf8, 0x23, 0x84,
         0x0e, 0xc6, 0xe2, 0xd0, 0xe3, 0x2f, 0x9e, 0x27, 0xce, 0x4d, 0x51, 0xfb,
         0x3e, 0xe1, 0x7c, 0x59, 0xd7, 0x02, 0x58, 0x20, 0xe4, 0xeb, 0x6b, 0x2d,
         0x3c, 0x36, 0xd3, 0x54, 0xfe, 0x1e, 0xe2, 0x81, 0x89, 0x29, 0x49, 0x79,
         0x82, 0x84, 0x82, 0xe3, 0x02, 0x64, 0x8b, 0x42, 0xd6, 0x4f, 0x89, 0xd8,
         0x97, 0x02, 0x2e, 0x11, 0x17, 0x58, 0x20, 0xa3, 0x13, 0xf2, 0x6c, 0xb6,
         0x25, 0xd2, 0x52, 0x20, 0x25, 0xf3, 0x33, 0x51, 0x17, 0x5c, 0xa0, 0x9a,
         0xe5, 0x4d, 0xa5, 0xee, 0x6a, 0x75, 0x05, 0xa5, 0x40, 0xef, 0x55, 0x3c,
         0xdd, 0x60, 0x29, 0x0b, 0x58, 0x20, 0x96, 0x47, 0xf0, 0xf8, 0xbc, 0x91,
         0xd5, 0xc2, 0xb2, 0x48, 0x17, 0xb6, 0xe0, 0x0d, 0x7a, 0xfb, 0x2f, 0xa4,
         0xe7, 0x8f, 0x4c, 0xce, 0xa3, 0x15, 0xeb, 0xfc, 0x01, 0xc4, 0x4a, 0xa0,
         0x13, 0xd6, 0x77, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31,
         0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0x2e, 0x61, 0x61, 0x6d,
         0x76, 0x61, 0xa6, 0x18, 0x19, 0x58, 0x20, 0xa6, 0xa9, 0x09, 0x5c, 0xa8,
         0x57, 0xc0, 0x29, 0x4d, 0x81, 0x50, 0x8b, 0x26, 0xd6, 0x59, 0xa7, 0x73,
         0xaf, 0x30, 0xfc, 0x24, 0x21, 0x5a, 0xb2, 0xdf, 0x92, 0xe2, 0x1e, 0x8f,
         0xe7, 0xbf, 0xfb, 0x05, 0x58, 0x20, 0xfc, 0xba, 0xdd, 0x35, 0xf1, 0x3a,
         0xf1, 0x53, 0xaa, 0xf1, 0x72, 0x7b, 0x3f, 0xd6, 0x86, 0x77, 0xfc, 0x5d,
         0x52, 0xbb, 0xef, 0xc0, 0xd7, 0x9d, 0x1c, 0x35, 0xf3, 0x47, 0xd6, 0xd0,
         0xce, 0x8c, 0x18, 0x23, 0x58, 0x20, 0x8b, 0x09, 0xae, 0xb3, 0xd6, 0x7d,
         0x66, 0xb9, 0xe2, 0x65, 0x5a, 0x4f, 0x4f, 0xe9, 0x0e, 0x2b, 0x16, 0x42,
         0x66, 0x81, 0x92, 0x7c, 0x05, 0xcb, 0x27, 0x8b, 0xe5, 0x4b, 0x6a, 0xca,
         0x89, 0x3e, 0x10, 0x58, 0x20, 0x9b, 0xcd, 0x90, 0x2b, 0x6b, 0xcf, 0x9d,
         0xa8, 0x70, 0xc1, 0x48, 0x4f, 0x14, 0x53, 0x23, 0xfc, 0x84, 0xa2, 0x64,
         0xd4, 0x22, 0x41, 0x37, 0x52, 0x1c, 0x03, 0x53, 0xc4, 0x8c, 0x22, 0xc3,
         0xab, 0x18, 0x24, 0x58, 0x20, 0x0f, 0xd3, 0xd9, 0x88, 0xef, 0x7c, 0x64,
         0x0c, 0xc4, 0x83, 0xf1, 0x23, 0xcb, 0x0b, 0xeb, 0x0c, 0x49, 0xaa, 0x6e,
         0xdd, 0xae, 0x96, 0xec, 0x17, 0xaf, 0x66, 0x58, 0x6b, 0x05, 0x16, 0x73,
         0x55, 0x18, 0x22, 0x58, 0x20, 0x45, 0x87, 0xe3, 0x6a, 0x16, 0x9a, 0x71,
         0xd6, 0xf2, 0x51, 0xfa, 0x39, 0x68, 0x69, 0x81, 0x22, 0x9e, 0x97, 0xe6,
         0x46, 0xc9, 0x84, 0x3c, 0x1b, 0x5a, 0x13, 0x91, 0xdb, 0x03, 0x6b, 0xd2,
         0xc0, 0x6d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x49,
         0x6e, 0x66, 0x6f, 0xa1, 0x69, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b,
         0x65, 0x79, 0xa4, 0x01, 0x02, 0x20, 0x01, 0x21, 0x58, 0x20, 0x53, 0x1a,
         0x86, 0xbd, 0xc5, 0xae, 0xa7, 0xb4, 0xbf, 0xf1, 0x12, 0x08, 0xef, 0xb5,
         0xc5, 0xa7, 0xdb, 0x1b, 0x00, 0x17, 0xc0, 0x20, 0xd7, 0xd9, 0xf6, 0xbf,
         0x89, 0x2d, 0x47, 0xa6, 0x17, 0xd1, 0x22, 0x58, 0x20, 0x24, 0xac, 0x72,
         0x74, 0xf7, 0xe6, 0xa1, 0x0a, 0x39, 0x56, 0xb9, 0x2e, 0x5a, 0xc2, 0xf8,
         0xab, 0x53, 0xcd, 0x8a, 0xb3, 0xb4, 0x38, 0x22, 0xab, 0x1d, 0x05, 0x90,
         0x4f, 0x3b, 0x50, 0xb2, 0xd6, 0x6c, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x69,
         0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0xa3, 0x66, 0x73, 0x69, 0x67, 0x6e,
         0x65, 0x64, 0xc0, 0x74, 0x32, 0x30, 0x32, 0x34, 0x2d, 0x30, 0x39, 0x2d,
         0x33, 0x30, 0x54, 0x31, 0x33, 0x3a, 0x35, 0x30, 0x3a, 0x30, 0x30, 0x5a,
         0x69, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0xc0, 0x74,
         0x32, 0x30, 0x32, 0x34, 0x2d, 0x30, 0x39, 0x2d, 0x33, 0x30, 0x54, 0x31,
         0x33, 0x3a, 0x35, 0x30, 0x3a, 0x30, 0x30, 0x5a, 0x6a, 0x76, 0x61, 0x6c,
         0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0xc0, 0x74, 0x32, 0x30, 0x32,
         0x34, 0x2d, 0x31, 0x30, 0x2d, 0x33, 0x30, 0x54, 0x31, 0x33, 0x3a, 0x35,
         0x30, 0x3a, 0x30, 0x30, 0x5a, 0x58, 0x40, 0xc9, 0x9b, 0xd4, 0x13, 0xf8,
         0x8d, 0xd3, 0xf1, 0x54, 0xe5, 0x82, 0x70, 0x10, 0xe3, 0x43, 0x14, 0xb4,
         0x41, 0x2d, 0x4d, 0x25, 0x1a, 0xed, 0x28, 0xda, 0xae, 0xe2, 0x23, 0x36,
         0x0a, 0x9d, 0xcc, 0x9f, 0xe9, 0x45, 0x08, 0x7e, 0x56, 0xc9, 0xa5, 0x4d,
         0xb6, 0x7a, 0x0a, 0xe3, 0xd7, 0xe1, 0xa6, 0x2e, 0xfa, 0x63, 0xc3, 0x09,
         0x74, 0x54, 0xa0, 0xe7, 0x82, 0xfa, 0x05, 0x77, 0xe5, 0xc4, 0xdc, 0x6c,
         0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64,
         0xa2, 0x6a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73,
         0xd8, 0x18, 0x41, 0xa0, 0x6a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41,
         0x75, 0x74, 0x68, 0xa1, 0x6f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53,
         0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x84, 0x43, 0xa1, 0x01,
         0x26, 0xa0, 0xf6, 0x58, 0x40, 0x83, 0xef, 0x0e, 0x35, 0xd8, 0xc5, 0x36,
         0x92, 0xb7, 0x1e, 0x9a, 0xea, 0x4c, 0x28, 0xab, 0xac, 0xd4, 0xf8, 0x50,
         0x1d, 0x49, 0x21, 0x82, 0x2d, 0x24, 0xd1, 0xd4, 0x82, 0x6b, 0x2e, 0x4e,
         0xe5, 0x0d, 0x2c, 0x3d, 0x35, 0xac, 0x4a, 0xf2, 0xdf, 0x95, 0x4e, 0x3a,
         0x29, 0x72, 0x8a, 0xad, 0xb7, 0x9b, 0xdd, 0x86, 0xc6, 0x25, 0xe9, 0xd6,
         0x7a, 0xb9, 0x0c, 0xbd, 0x7c, 0xd0, 0x7a, 0xe1, 0x3c, 0x66, 0x73, 0x74,
         0x61, 0x74, 0x75, 0x73, 0x00},
    },
    // Google IACA identity-pass example
    {StaticString(
         "0x5070e8199f3c5e1ecf6249662994997605b7205b039ff77fad4f9aceb3cadbcd"),
     StaticString(
         "0xc2f6fa056f2423ca57f828ded12656bdae863f23654561d494ae5efa9feb883f"),
     {0x83, 0xf6, 0xf6, 0x84, 0x71, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72,
      0x48, 0x61, 0x6e, 0x64, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x31, 0x58, 0x37,
      0x01, 0x06, 0x78, 0x53, 0x3e, 0x47, 0xa7, 0xdb, 0x12, 0xde, 0x39, 0x1a,
      0x58, 0xa0, 0x53, 0x1a, 0x13, 0x3e, 0x8f, 0x07, 0x14, 0xee, 0x07, 0x5f,
      0xcb, 0x38, 0x4a, 0xf9, 0x0a, 0xb1, 0x71, 0x31, 0xce, 0x24, 0x1e, 0x7b,
      0xf0, 0x71, 0x98, 0x32, 0xb5, 0xad, 0x02, 0xac, 0xee, 0x72, 0xfd, 0x69,
      0x0e, 0xf3, 0x45, 0x28, 0x09, 0x71, 0xde, 0x58, 0x49, 0xa3, 0x63, 0x63,
      0x61, 0x74, 0x01, 0x64, 0x74, 0x79, 0x70, 0x65, 0x01, 0x67, 0x64, 0x65,
      0x74, 0x61, 0x69, 0x6c, 0x73, 0xa1, 0x67, 0x62, 0x61, 0x73, 0x65, 0x55,
      0x72, 0x6c, 0x78, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
      0x6f, 0x76, 0x32, 0x2d, 0x74, 0x64, 0x6d, 0x67, 0x70, 0x77, 0x61, 0x6d,
      0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x72, 0x70, 0x2e,
      0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x58, 0x20,
      0xc5, 0x86, 0xef, 0x29, 0xc7, 0xf0, 0x76, 0xfc, 0x15, 0x2b, 0x93, 0x85,
      0x94, 0x7e, 0x82, 0x58, 0x9d, 0x6d, 0xe2, 0x43, 0xae, 0x18, 0x90, 0xef,
      0x08, 0x5f, 0xd1, 0x31, 0x2b, 0x1f, 0x25, 0xdd},
     188,
     (uint8_t *)"2025-01-29T18:00:00Z",
     kIDPass,
     2211,
     {0xa3, 0x67, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e,
      0x30, 0x69, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x81,
      0xa3, 0x67, 0x64, 0x6f, 0x63, 0x54, 0x79, 0x70, 0x65, 0x78, 0x1a, 0x63,
      0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x77, 0x61,
      0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x69, 0x64, 0x63, 0x61, 0x72, 0x64, 0x2e,
      0x31, 0x6c, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e,
      0x65, 0x64, 0xa2, 0x6a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x70, 0x61, 0x63,
      0x65, 0x73, 0xa1, 0x71, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e,
      0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0x81, 0xd8, 0x18,
      0x58, 0x5d, 0xa4, 0x68, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x49, 0x44,
      0x19, 0xeb, 0xa5, 0x66, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x50, 0xfa,
      0xe2, 0x65, 0x0e, 0x5a, 0x8a, 0x33, 0x39, 0x2b, 0xc5, 0x74, 0x26, 0x6f,
      0x4d, 0x19, 0xa7, 0x71, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x49,
      0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x6a, 0x62, 0x69,
      0x72, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x6c, 0x65,
      0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0xd9, 0x03, 0xec,
      0x6a, 0x31, 0x39, 0x39, 0x38, 0x2d, 0x30, 0x39, 0x2d, 0x30, 0x34, 0x6a,
      0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x84, 0x43,
      0xa1, 0x01, 0x26, 0xa1, 0x18, 0x21, 0x59, 0x03, 0x91, 0x30, 0x82, 0x03,
      0x8d, 0x30, 0x82, 0x03, 0x12, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x13,
      0x35, 0xa5, 0x2a, 0x85, 0x87, 0x44, 0xb0, 0x01, 0x08, 0x5b, 0xe6, 0xaa,
      0x4c, 0x72, 0xf8, 0xc8, 0x8f, 0x04, 0x97, 0x30, 0x0a, 0x06, 0x08, 0x2a,
      0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03, 0x30, 0x5d, 0x31, 0x0b, 0x30,
      0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x54, 0x31, 0x13,
      0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f,
      0x67, 0x6c, 0x65, 0x20, 0x4c, 0x4c, 0x43, 0x31, 0x0f, 0x30, 0x0d, 0x06,
      0x03, 0x55, 0x04, 0x0b, 0x13, 0x06, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74,
      0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1f, 0x54,
      0x45, 0x53, 0x54, 0x20, 0x55, 0x53, 0x45, 0x20, 0x4f, 0x4e, 0x4c, 0x59,
      0x20, 0x47, 0x49, 0x43, 0x49, 0x20, 0x53, 0x74, 0x61, 0x67, 0x69, 0x6e,
      0x67, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x34,
      0x30, 0x33, 0x32, 0x38, 0x30, 0x31, 0x32, 0x32, 0x30, 0x30, 0x5a, 0x17,
      0x0d, 0x32, 0x35, 0x30, 0x33, 0x32, 0x38, 0x30, 0x31, 0x32, 0x31, 0x35,
      0x39, 0x5a, 0x30, 0x5b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
      0x06, 0x13, 0x02, 0x55, 0x54, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
      0x04, 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x4c,
      0x4c, 0x43, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
      0x06, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x31, 0x26, 0x30, 0x24, 0x06,
      0x03, 0x55, 0x04, 0x03, 0x13, 0x1d, 0x54, 0x45, 0x53, 0x54, 0x20, 0x55,
      0x53, 0x45, 0x20, 0x4f, 0x4e, 0x4c, 0x59, 0x20, 0x47, 0x49, 0x43, 0x49,
      0x20, 0x53, 0x74, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x20, 0x44, 0x53, 0x30,
      0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
      0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42,
      0x00, 0x04, 0x50, 0x70, 0xe8, 0x19, 0x9f, 0x3c, 0x5e, 0x1e, 0xcf, 0x62,
      0x49, 0x66, 0x29, 0x94, 0x99, 0x76, 0x05, 0xb7, 0x20, 0x5b, 0x03, 0x9f,
      0xf7, 0x7f, 0xad, 0x4f, 0x9a, 0xce, 0xb3, 0xca, 0xdb, 0xcd, 0xc2, 0xf6,
      0xfa, 0x05, 0x6f, 0x24, 0x23, 0xca, 0x57, 0xf8, 0x28, 0xde, 0xd1, 0x26,
      0x56, 0xbd, 0xae, 0x86, 0x3f, 0x23, 0x65, 0x45, 0x61, 0xd4, 0x94, 0xae,
      0x5e, 0xfa, 0x9f, 0xeb, 0x88, 0x3f, 0xa3, 0x82, 0x01, 0xb1, 0x30, 0x82,
      0x01, 0xad, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
      0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
      0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03,
      0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x62, 0x1b, 0xd1, 0x8c, 0xa5,
      0x64, 0x49, 0x37, 0x2b, 0x6f, 0x0c, 0xf7, 0x82, 0x17, 0xac, 0x8a, 0x6d,
      0xa3, 0xde, 0x67, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
      0x30, 0x16, 0x80, 0x14, 0x9f, 0x5a, 0x68, 0xc3, 0xe9, 0x80, 0x19, 0xde,
      0xe9, 0x98, 0x25, 0x64, 0xf6, 0xba, 0x23, 0x84, 0xff, 0x92, 0xed, 0xb4,
      0x30, 0x81, 0x8d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
      0x01, 0x04, 0x81, 0x80, 0x30, 0x7e, 0x30, 0x7c, 0x06, 0x08, 0x2b, 0x06,
      0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x70, 0x68, 0x74, 0x74, 0x70,
      0x3a, 0x2f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x63, 0x61,
      0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x36, 0x36, 0x30,
      0x33, 0x63, 0x36, 0x34, 0x37, 0x2d, 0x30, 0x30, 0x30, 0x30, 0x2d, 0x32,
      0x39, 0x64, 0x34, 0x2d, 0x61, 0x65, 0x61, 0x36, 0x2d, 0x66, 0x34, 0x66,
      0x35, 0x65, 0x38, 0x30, 0x64, 0x31, 0x62, 0x39, 0x30, 0x2e, 0x73, 0x74,
      0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
      0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x34, 0x31, 0x64,
      0x34, 0x33, 0x36, 0x38, 0x35, 0x39, 0x65, 0x34, 0x35, 0x64, 0x62, 0x66,
      0x33, 0x65, 0x35, 0x61, 0x39, 0x2f, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x74,
      0x30, 0x81, 0x82, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x7b, 0x30, 0x79,
      0x30, 0x77, 0xa0, 0x75, 0xa0, 0x73, 0x86, 0x71, 0x68, 0x74, 0x74, 0x70,
      0x3a, 0x2f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x63, 0x61,
      0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x36, 0x36, 0x30,
      0x33, 0x63, 0x36, 0x34, 0x37, 0x2d, 0x30, 0x30, 0x30, 0x30, 0x2d, 0x32,
      0x39, 0x64, 0x34, 0x2d, 0x61, 0x65, 0x61, 0x36, 0x2d, 0x66, 0x34, 0x66,
      0x35, 0x65, 0x38, 0x30, 0x64, 0x31, 0x62, 0x39, 0x30, 0x2e, 0x73, 0x74,
      0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
      0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x34, 0x31, 0x64,
      0x34, 0x33, 0x36, 0x38, 0x35, 0x39, 0x65, 0x34, 0x35, 0x64, 0x62, 0x66,
      0x33, 0x65, 0x35, 0x61, 0x39, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x72,
      0x6c, 0x30, 0x21, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, 0x1a, 0x30, 0x18,
      0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
      0x77, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
      0x30, 0x15, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x0b,
      0x30, 0x09, 0x06, 0x07, 0x28, 0x81, 0x8c, 0x5d, 0x05, 0x01, 0x02, 0x30,
      0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03, 0x03,
      0x69, 0x00, 0x30, 0x66, 0x02, 0x31, 0x00, 0xfb, 0xa3, 0xb8, 0xf1, 0x2d,
      0x58, 0xc6, 0xdf, 0x20, 0xbc, 0xa2, 0x6a, 0x6c, 0x2e, 0x9c, 0x30, 0x9e,
      0x26, 0xf9, 0x75, 0x85, 0x16, 0x95, 0x71, 0xab, 0x11, 0xf1, 0x35, 0xb0,
      0x4c, 0xd8, 0x19, 0x24, 0x6b, 0x24, 0x3f, 0xc0, 0xdc, 0x89, 0xab, 0x65,
      0x4a, 0x4a, 0xe7, 0xc9, 0x25, 0xf5, 0x32, 0x02, 0x31, 0x00, 0xf9, 0xe0,
      0xd4, 0x0a, 0xfc, 0x35, 0x6f, 0x74, 0x2d, 0xb0, 0x65, 0x7e, 0x64, 0xd7,
      0xc8, 0x1f, 0x12, 0x64, 0x3c, 0x39, 0xc1, 0xeb, 0x3e, 0x6a, 0xf9, 0x0c,
      0xd6, 0x24, 0x4e, 0xbf, 0x0d, 0x1a, 0xb7, 0x7a, 0x38, 0x01, 0x84, 0x5f,
      0x0e, 0xcf, 0xc8, 0x69, 0x9c, 0x7c, 0xa0, 0xef, 0xf5, 0xde, 0x59, 0x03,
      0x62, 0xd8, 0x18, 0x59, 0x03, 0x5d, 0xa6, 0x67, 0x76, 0x65, 0x72, 0x73,
      0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e, 0x30, 0x6f, 0x64, 0x69, 0x67, 0x65,
      0x73, 0x74, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x67,
      0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x6c, 0x76, 0x61, 0x6c, 0x75,
      0x65, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0xa2, 0x71, 0x6f, 0x72,
      0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
      0x35, 0x2e, 0x31, 0xad, 0x19, 0x11, 0x9e, 0x58, 0x20, 0x0e, 0xc0, 0x48,
      0x3e, 0x52, 0xb7, 0xe5, 0xb1, 0xa3, 0xba, 0xd7, 0x2d, 0xc3, 0x53, 0x13,
      0x9b, 0x10, 0x83, 0xc0, 0x48, 0x37, 0xe0, 0xab, 0x2d, 0x62, 0x24, 0xb0,
      0x31, 0xeb, 0xed, 0x79, 0x8f, 0x19, 0x29, 0xfa, 0x58, 0x20, 0x14, 0x6c,
      0x91, 0x85, 0x3b, 0x61, 0xf0, 0xbd, 0x9d, 0x88, 0x93, 0x61, 0xfa, 0x40,
      0x23, 0xe6, 0x22, 0xfe, 0x5b, 0x6a, 0x20, 0xaa, 0xd4, 0xa0, 0xad, 0x2a,
      0x58, 0x48, 0x5e, 0xdf, 0xa3, 0x17, 0x19, 0x3f, 0x01, 0x58, 0x20, 0xfc,
      0xb0, 0x96, 0xe0, 0x4d, 0xbe, 0x0e, 0xd5, 0x7a, 0xfe, 0x9a, 0x3c, 0x94,
      0xd5, 0xca, 0x1f, 0x2e, 0x4f, 0x42, 0xf6, 0xa8, 0xae, 0xb6, 0x6a, 0x30,
      0x5a, 0x73, 0xe8, 0x02, 0xf5, 0xd8, 0x0d, 0x19, 0x45, 0x0e, 0x58, 0x20,
      0x4d, 0x55, 0x56, 0xdf, 0xe9, 0x6d, 0x0b, 0x4d, 0xd8, 0x3a, 0x03, 0xe5,
      0x17, 0xe0, 0xa7, 0x85, 0xe5, 0xf6, 0x74, 0xb3, 0x87, 0xdc, 0xc8, 0xa6,
      0x6c, 0x1b, 0x2f, 0x07, 0x87, 0x32, 0x44, 0x6c, 0x19, 0x4b, 0x15, 0x58,
      0x20, 0x5a, 0x4e, 0xd4, 0xb6, 0x25, 0xa1, 0x67, 0x08, 0xa0, 0x02, 0xee,
      0x8e, 0xfd, 0xfa, 0xa8, 0x34, 0x69, 0xe4, 0x28, 0xe2, 0x5e, 0x4b, 0xfe,
      0xc2, 0xc3, 0xb6, 0xef, 0x7d, 0x5a, 0x98, 0xd3, 0xab, 0x19, 0x4b, 0xf9,
      0x58, 0x20, 0x1f, 0xf4, 0x99, 0xcc, 0x18, 0x3a, 0xc8, 0xbb, 0x57, 0xce,
      0x2d, 0xd6, 0x1d, 0xa2, 0x5e, 0x13, 0xe3, 0xc0, 0x71, 0xf1, 0xe9, 0x66,
      0xbf, 0xf8, 0xc4, 0xa4, 0xab, 0x72, 0xa4, 0xc9, 0x31, 0x75, 0x19, 0x63,
      0x99, 0x58, 0x20, 0xf9, 0x3d, 0xce, 0xdd, 0x21, 0x16, 0x53, 0xd9, 0x9c,
      0x8a, 0x62, 0x70, 0x29, 0x10, 0xc5, 0x27, 0x57, 0x97, 0x24, 0x68, 0x57,
      0xf8, 0x2a, 0xbe, 0x9c, 0xd5, 0xcd, 0xc0, 0x78, 0x36, 0x56, 0x74, 0x19,
      0x65, 0x90, 0x58, 0x20, 0xa3, 0xf5, 0xc6, 0xc4, 0x74, 0x6a, 0xb5, 0x9d,
      0xae, 0x8c, 0x43, 0x26, 0xf8, 0x00, 0xac, 0x25, 0xf3, 0xd8, 0x45, 0x3e,
      0xf2, 0x14, 0x1e, 0x1c, 0x4f, 0x24, 0x46, 0x11, 0x8f, 0x7b, 0x59, 0xba,
      0x19, 0x6e, 0xfb, 0x58, 0x20, 0xe1, 0x85, 0x25, 0xb6, 0x6f, 0xc9, 0x72,
      0x86, 0x1d, 0x84, 0xc5, 0x7c, 0x60, 0x1e, 0x3b, 0xfb, 0x9f, 0x05, 0x4c,
      0x8c, 0x1c, 0xb6, 0xb4, 0xb9, 0x50, 0xa1, 0x27, 0x7f, 0xb1, 0x13, 0xc7,
      0x9a, 0x19, 0x94, 0xe0, 0x58, 0x20, 0xa5, 0x61, 0x36, 0x4f, 0x92, 0x27,
      0xa5, 0x8c, 0xfb, 0xfd, 0xbf, 0x7c, 0xb2, 0x29, 0x17, 0x0c, 0xd0, 0x5c,
      0x7b, 0x10, 0x9c, 0x7a, 0xa5, 0x1d, 0xa6, 0x76, 0x81, 0x72, 0xb9, 0x1f,
      0x7f, 0x57, 0x19, 0x97, 0xbd, 0x58, 0x20, 0x04, 0xd5, 0xbe, 0xd8, 0x7e,
      0x03, 0xb9, 0x08, 0xd2, 0x64, 0x76, 0x2d, 0x8f, 0x8d, 0xd6, 0x41, 0x2b,
      0x61, 0xbb, 0xa4, 0x3d, 0xd7, 0x08, 0x70, 0x8b, 0xdd, 0x96, 0x94, 0x4d,
      0xc4, 0xce, 0x5e, 0x19, 0xe1, 0x7b, 0x58, 0x20, 0x69, 0xcb, 0x57, 0x76,
      0xe1, 0x2c, 0xe1, 0xe0, 0x06, 0x31, 0xbd, 0x8f, 0x76, 0x54, 0x99, 0x34,
      0x4f, 0x27, 0x0f, 0x64, 0xd6, 0x45, 0xc6, 0x08, 0x88, 0x77, 0xa0, 0x3f,
      0xa3, 0x90, 0x2e, 0x87, 0x19, 0xeb, 0xa5, 0x58, 0x20, 0x99, 0x03, 0xd0,
      0x2a, 0xbd, 0xc6, 0x12, 0xc3, 0xde, 0xe7, 0x52, 0xbd, 0x88, 0x62, 0xed,
      0xe0, 0xe3, 0x86, 0x8e, 0xe2, 0xa2, 0x0f, 0x5d, 0xc8, 0xdb, 0x03, 0x1a,
      0x5f, 0x57, 0xd7, 0x5e, 0x4a, 0x78, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x67,
      0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74,
      0x2e, 0x69, 0x64, 0x63, 0x61, 0x72, 0x64, 0x2e, 0x31, 0xa1, 0x19, 0x32,
      0xab, 0x58, 0x20, 0xbb, 0x51, 0x2c, 0x61, 0x32, 0xd0, 0x3a, 0x2f, 0x13,
      0xb7, 0x6a, 0x84, 0x37, 0x55, 0x82, 0xe7, 0x1d, 0xfa, 0x49, 0x76, 0x89,
      0x81, 0xfa, 0xba, 0xf5, 0x3f, 0x84, 0x34, 0x01, 0x6b, 0x5e, 0x7b, 0x6d,
      0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x49, 0x6e, 0x66,
      0x6f, 0xa1, 0x69, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79,
      0xa4, 0x01, 0x02, 0x20, 0x01, 0x21, 0x58, 0x20, 0x84, 0x59, 0x27, 0x1c,
      0xa2, 0x99, 0x4e, 0x77, 0x51, 0x7b, 0x74, 0x3f, 0x76, 0xfd, 0xbb, 0x81,
      0x79, 0xad, 0xf1, 0x31, 0x31, 0xbb, 0x48, 0x7e, 0x93, 0x9b, 0xd1, 0x7b,
      0xc7, 0xe8, 0xf5, 0x43, 0x22, 0x58, 0x20, 0x69, 0x0b, 0x7b, 0xf6, 0x3f,
      0x8d, 0x14, 0x69, 0x5b, 0xc5, 0xd5, 0xcb, 0x4a, 0x82, 0x23, 0xfb, 0x7b,
      0xee, 0x6d, 0xdd, 0x08, 0x7d, 0x9e, 0x3f, 0x06, 0xfc, 0x16, 0xff, 0xe8,
      0x84, 0xce, 0x83, 0x67, 0x64, 0x6f, 0x63, 0x54, 0x79, 0x70, 0x65, 0x78,
      0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
      0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x69, 0x64, 0x63, 0x61, 0x72,
      0x64, 0x2e, 0x31, 0x6c, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x69, 0x74, 0x79,
      0x49, 0x6e, 0x66, 0x6f, 0xa3, 0x66, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64,
      0xc0, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2d, 0x30, 0x31, 0x2d, 0x32, 0x39,
      0x54, 0x31, 0x34, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x38, 0x5a, 0x69, 0x76,
      0x61, 0x6c, 0x69, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0xc0, 0x74, 0x32, 0x30,
      0x32, 0x35, 0x2d, 0x30, 0x31, 0x2d, 0x32, 0x39, 0x54, 0x31, 0x34, 0x3a,
      0x33, 0x39, 0x3a, 0x32, 0x38, 0x5a, 0x6a, 0x76, 0x61, 0x6c, 0x69, 0x64,
      0x55, 0x6e, 0x74, 0x69, 0x6c, 0xc0, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2d,
      0x30, 0x32, 0x2d, 0x31, 0x32, 0x54, 0x31, 0x34, 0x3a, 0x33, 0x39, 0x3a,
      0x32, 0x38, 0x5a, 0x58, 0x40, 0x12, 0xb3, 0x7a, 0x55, 0xcb, 0xdc, 0xda,
      0x03, 0xa0, 0x6e, 0xa0, 0x60, 0xd2, 0x44, 0xab, 0x5b, 0x1a, 0x10, 0xe4,
      0x29, 0x50, 0xed, 0x84, 0xdf, 0x9e, 0xce, 0xf7, 0xbf, 0xb0, 0x0d, 0xb0,
      0xfb, 0xa8, 0x4e, 0xa3, 0x86, 0x43, 0x22, 0x8c, 0xa2, 0x91, 0x4e, 0x25,
      0x1f, 0x5d, 0x37, 0x65, 0xf2, 0x1e, 0x45, 0x33, 0x0e, 0xe7, 0x2b, 0xd8,
      0x05, 0x28, 0x99, 0x76, 0xb7, 0xa6, 0x1b, 0x3f, 0x52, 0x6c, 0x64, 0x65,
      0x76, 0x69, 0x63, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xa2, 0x6a,
      0x6e, 0x61, 0x6d, 0x65, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0xd8, 0x18,
      0x41, 0xa0, 0x6a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74,
      0x68, 0xa1, 0x6f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x69, 0x67,
      0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0,
      0xf6, 0x58, 0x40, 0x3f, 0xae, 0x1b, 0xb1, 0x4c, 0x0f, 0x22, 0xb3, 0x73,
      0x26, 0xf4, 0x4d, 0x61, 0x2f, 0x59, 0x94, 0x35, 0x39, 0xa6, 0x0b, 0xe8,
      0xd4, 0xa6, 0x8f, 0x0b, 0x0a, 0x66, 0x19, 0xea, 0x3a, 0xbf, 0x56, 0xdb,
      0xc3, 0xe2, 0xeb, 0x1e, 0xcd, 0xa8, 0x65, 0x2f, 0x62, 0xc5, 0xd4, 0x44,
      0x3f, 0x2c, 0x1b, 0xde, 0x9a, 0xd5, 0xd9, 0x2c, 0xaf, 0x16, 0xbc, 0xdf,
      0x0e, 0x7c, 0x40, 0xd8, 0x5b, 0xf5, 0xf3, 0x66, 0x73, 0x74, 0x61, 0x74,
      0x75, 0x73, 0x00}},
    // Mdoc example from website explainer.
    {StaticString(
         "0xb4682ec20e06e8df840b5dd32959798ab20c544d4da50109ff4684d06fd261fc"),
     StaticString(
         "0xf6f8e9a811911329a5f653fcec5990092c91a65bc1695d291cd51de9c94e7db7"),
     {0x83, 0xF6, 0xF6, 0x84, 0x71, 0x42, 0x72, 0x6F, 0x77, 0x73, 0x65, 0x72,
      0x48, 0x61, 0x6E, 0x64, 0x6F, 0x76, 0x65, 0x72, 0x76, 0x31, 0x58, 0x20,
      0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
      0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
      0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x58, 0x35, 0xA3, 0x63,
      0x63, 0x61, 0x74, 0x01, 0x64, 0x74, 0x79, 0x70, 0x65, 0x01, 0x67, 0x64,
      0x65, 0x74, 0x61, 0x69, 0x6C, 0x73, 0xA1, 0x67, 0x62, 0x61, 0x73, 0x65,
      0x55, 0x72, 0x6C, 0x77, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x72,
      0x65, 0x6C, 0x79, 0x69, 0x6E, 0x67, 0x70, 0x61, 0x72, 0x74, 0x79, 0x2E,
      0x63, 0x6F, 0x6D, 0x58, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
      0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33,
      0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
      0x40},
     145,
     (uint8_t *)"2025-04-30T00:00:00Z",
     kMDL,
     1298,
     {0xa3, 0x67, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e,
      0x30, 0x69, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x81,
      0xa3, 0x67, 0x64, 0x6f, 0x63, 0x54, 0x79, 0x70, 0x65, 0x75, 0x6f, 0x72,
      0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
      0x35, 0x2e, 0x31, 0x2e, 0x6d, 0x44, 0x4c, 0x6c, 0x69, 0x73, 0x73, 0x75,
      0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xa2, 0x6a, 0x6e, 0x61,
      0x6d, 0x65, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0xa1, 0x71, 0x6f, 0x72,
      0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
      0x35, 0x2e, 0x31, 0x82, 0xd8, 0x18, 0x58, 0x60, 0xa4, 0x68, 0x64, 0x69,
      0x67, 0x65, 0x73, 0x74, 0x49, 0x44, 0x00, 0x66, 0x72, 0x61, 0x6e, 0x64,
      0x6f, 0x6d, 0x58, 0x20, 0x22, 0x31, 0x18, 0x3c, 0x7d, 0x9f, 0x4d, 0x2e,
      0x2e, 0x14, 0x6d, 0x24, 0x84, 0x58, 0xb4, 0xcc, 0xe9, 0x48, 0x79, 0x8f,
      0xda, 0x82, 0x40, 0xd3, 0x60, 0xa8, 0xf4, 0x0f, 0x6d, 0x75, 0x48, 0xd3,
      0x71, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x65, 0x6e,
      0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6f,
      0x76, 0x65, 0x72, 0x5f, 0x31, 0x38, 0x6c, 0x65, 0x6c, 0x65, 0x6d, 0x65,
      0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0xf5, 0xd8, 0x18, 0x58, 0x5f,
      0xa4, 0x68, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x49, 0x44, 0x01, 0x66,
      0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x58, 0x20, 0xcd, 0x6b, 0x77, 0x63,
      0x8d, 0x8f, 0x26, 0x18, 0xb7, 0x2f, 0x90, 0x25, 0xb8, 0x8c, 0x3d, 0x63,
      0x2a, 0xbb, 0xa8, 0xde, 0x96, 0xe3, 0x93, 0x2d, 0xa3, 0x56, 0x6e, 0x49,
      0x1f, 0x19, 0xcb, 0x91, 0x71, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
      0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x63, 0x6e,
      0x79, 0x6d, 0x6c, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61,
      0x6c, 0x75, 0x65, 0x67, 0x31, 0x32, 0x33, 0x31, 0x32, 0x34, 0x34, 0x6a,
      0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x84, 0x43,
      0xa1, 0x01, 0x26, 0xa1, 0x18, 0x21, 0x59, 0x01, 0x83, 0x30, 0x82, 0x01,
      0x7f, 0x30, 0x82, 0x01, 0x26, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01,
      0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03,
      0x02, 0x30, 0x29, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a,
      0x13, 0x08, 0x61, 0x62, 0x68, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x31, 0x14,
      0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0b, 0x54, 0x65, 0x73,
      0x74, 0x69, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x79, 0x30, 0x1e, 0x17, 0x0d,
      0x32, 0x35, 0x30, 0x34, 0x32, 0x38, 0x32, 0x31, 0x31, 0x30, 0x31, 0x34,
      0x5a, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x35, 0x30, 0x35, 0x32, 0x31, 0x31,
      0x30, 0x31, 0x34, 0x5a, 0x30, 0x29, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03,
      0x55, 0x04, 0x0a, 0x13, 0x08, 0x61, 0x62, 0x68, 0x76, 0x69, 0x6f, 0x75,
      0x73, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0b,
      0x54, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x79, 0x30,
      0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
      0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42,
      0x00, 0x04, 0xb4, 0x68, 0x2e, 0xc2, 0x0e, 0x06, 0xe8, 0xdf, 0x84, 0x0b,
      0x5d, 0xd3, 0x29, 0x59, 0x79, 0x8a, 0xb2, 0x0c, 0x54, 0x4d, 0x4d, 0xa5,
      0x01, 0x09, 0xff, 0x46, 0x84, 0xd0, 0x6f, 0xd2, 0x61, 0xfc, 0xf6, 0xf8,
      0xe9, 0xa8, 0x11, 0x91, 0x13, 0x29, 0xa5, 0xf6, 0x53, 0xfc, 0xec, 0x59,
      0x90, 0x09, 0x2c, 0x91, 0xa6, 0x5b, 0xc1, 0x69, 0x5d, 0x29, 0x1c, 0xd5,
      0x1d, 0xe9, 0xc9, 0x4e, 0x7d, 0xb7, 0xa3, 0x3f, 0x30, 0x3d, 0x30, 0x0e,
      0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
      0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30,
      0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06,
      0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0c, 0x06,
      0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30,
      0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03,
      0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x31, 0x0f, 0x9a, 0xc7, 0x56, 0xcf,
      0xfa, 0x49, 0x3d, 0x2f, 0x75, 0x77, 0xab, 0x38, 0x0e, 0x9d, 0x4d, 0xa6,
      0x91, 0xeb, 0x5a, 0x8f, 0x19, 0xeb, 0xb2, 0xd9, 0x39, 0xa1, 0x67, 0x49,
      0x1f, 0xbe, 0x02, 0x20, 0x4c, 0x7d, 0xac, 0xde, 0x12, 0xf6, 0x98, 0xb1,
      0xf9, 0x6e, 0xa1, 0xc9, 0xbc, 0xa1, 0x3e, 0xd7, 0x9c, 0xd5, 0x67, 0xf4,
      0x04, 0x47, 0x3d, 0xa0, 0x66, 0x05, 0xa8, 0xf1, 0x49, 0xef, 0x67, 0x01,
      0x59, 0x01, 0x7f, 0xd8, 0x18, 0x59, 0x01, 0x7a, 0xa6, 0x67, 0x76, 0x65,
      0x72, 0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e, 0x30, 0x6f, 0x64, 0x69,
      0x67, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
      0x6d, 0x67, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x67, 0x64, 0x6f,
      0x63, 0x54, 0x79, 0x70, 0x65, 0x75, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73,
      0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0x2e,
      0x6d, 0x44, 0x4c, 0x6c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x69, 0x67,
      0x65, 0x73, 0x74, 0x73, 0xa1, 0x71, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73,
      0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0xa2,
      0x00, 0x58, 0x20, 0xff, 0x75, 0x3b, 0xf6, 0xc1, 0x19, 0x63, 0xfd, 0x64,
      0x4c, 0xcc, 0xe4, 0x35, 0x56, 0xcd, 0xe1, 0x85, 0x7a, 0xcd, 0xfd, 0x8e,
      0x7e, 0x60, 0x7c, 0x01, 0xdf, 0xf5, 0xdc, 0x3e, 0x1a, 0xfb, 0xfc, 0x01,
      0x58, 0x20, 0xc5, 0x70, 0x2c, 0x25, 0x26, 0x38, 0xa4, 0xd0, 0xe9, 0x77,
      0x44, 0xcd, 0x38, 0x4f, 0x0c, 0x04, 0x26, 0x07, 0x94, 0x84, 0xbe, 0xa1,
      0xa2, 0xc9, 0x4b, 0xe5, 0x8f, 0xb5, 0xe8, 0xc2, 0x28, 0x61, 0x6d, 0x64,
      0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x49, 0x6e, 0x66, 0x6f,
      0xa1, 0x69, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0xa4,
      0x01, 0x02, 0x20, 0x01, 0x21, 0x58, 0x20, 0x02, 0x7a, 0x5a, 0x1f, 0xa9,
      0x15, 0x1f, 0x67, 0xcd, 0xd2, 0xc5, 0x7f, 0xaf, 0x84, 0xcc, 0xfd, 0xb0,
      0x5b, 0xcf, 0x59, 0x07, 0x87, 0x65, 0xfa, 0x0b, 0x7e, 0x1a, 0x82, 0x23,
      0x79, 0x0d, 0xd2, 0x22, 0x58, 0x20, 0x3e, 0x17, 0x08, 0x11, 0xbe, 0xcf,
      0x18, 0x4d, 0x36, 0xba, 0x38, 0xdb, 0xc2, 0x29, 0x21, 0x7d, 0x4f, 0xac,
      0xa0, 0xf1, 0x62, 0xd5, 0x54, 0xe5, 0xf1, 0x40, 0x8d, 0x59, 0xc4, 0xba,
      0xaa, 0x0b, 0x6c, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x69, 0x74, 0x79, 0x49,
      0x6e, 0x66, 0x6f, 0xa3, 0x66, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xc0,
      0x74, 0x32, 0x30, 0x32, 0x35, 0x2d, 0x30, 0x34, 0x2d, 0x32, 0x38, 0x54,
      0x32, 0x31, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x5a, 0x69, 0x76, 0x61,
      0x6c, 0x69, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0xc0, 0x74, 0x32, 0x30, 0x32,
      0x35, 0x2d, 0x30, 0x34, 0x2d, 0x32, 0x38, 0x54, 0x32, 0x31, 0x3a, 0x30,
      0x30, 0x3a, 0x30, 0x30, 0x5a, 0x6a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55,
      0x6e, 0x74, 0x69, 0x6c, 0xc0, 0x74, 0x32, 0x30, 0x32, 0x36, 0x2d, 0x30,
      0x34, 0x2d, 0x32, 0x38, 0x54, 0x32, 0x31, 0x3a, 0x30, 0x30, 0x3a, 0x30,
      0x30, 0x5a, 0x58, 0x40, 0x29, 0x22, 0x9f, 0xa5, 0xda, 0x13, 0x7c, 0x73,
      0xfa, 0xe1, 0xe5, 0xcb, 0x87, 0x5b, 0xa0, 0xd2, 0x21, 0x3d, 0x0d, 0xbe,
      0xdc, 0x9d, 0x08, 0x96, 0xa7, 0x45, 0x99, 0x08, 0x83, 0x72, 0xd8, 0x98,
      0x83, 0x45, 0x05, 0xb8, 0x6d, 0x3a, 0xde, 0x7e, 0xad, 0x4f, 0xc9, 0x3a,
      0xa2, 0x8c, 0xd5, 0xc4, 0xf3, 0xff, 0x2d, 0xd4, 0x89, 0xfe, 0x89, 0x9a,
      0x71, 0xe7, 0x21, 0x13, 0xae, 0x74, 0xfa, 0x8f, 0x6c, 0x64, 0x65, 0x76,
      0x69, 0x63, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xa2, 0x6a, 0x6e,
      0x61, 0x6d, 0x65, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0xd8, 0x18, 0x41,
      0xa0, 0x6a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68,
      0xa1, 0x6f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x69, 0x67, 0x6e,
      0x61, 0x74, 0x75, 0x72, 0x65, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0xf6,
      0x58, 0x40, 0x52, 0x0b, 0x17, 0xb8, 0xbd, 0x7e, 0xae, 0x5d, 0x4e, 0x7d,
      0xcd, 0xb8, 0x8c, 0xdd, 0xb2, 0x4e, 0x7b, 0x2b, 0xf6, 0x5a, 0xfe, 0x42,
      0x0a, 0x1c, 0x48, 0x81, 0x2d, 0x97, 0x65, 0x78, 0x42, 0xba, 0xb5, 0x05,
      0x74, 0xf5, 0xc8, 0x29, 0xc3, 0x2f, 0xa6, 0xd7, 0xa2, 0x28, 0xdb, 0x96,
      0x64, 0xdc, 0x75, 0x4e, 0x2f, 0x6c, 0x90, 0x1c, 0x7b, 0x9a, 0x97, 0x49,
      0x59, 0xfc, 0x9b, 0x35, 0xcb, 0x4c, 0x66, 0x73, 0x74, 0x61, 0x74, 0x75,
      0x73, 0x00}},
    // Large mDoc from Canonical Playground June 2025
    {StaticString(
         "0xab4bb1a19d6bc9d61138cf43a004ba5156ae4f3080804c910cf638eacf3b4e00"),
     StaticString(
         "0x216a667060dae40ecca6fb4b39f1c93e1098ac64667b6e5bbf0140518672cb7b"),
     {0x83, 0xf6, 0xf6, 0x82, 0x76, 0x4f, 0x70, 0x65, 0x6e,
      0x49, 0x44, 0x34, 0x56, 0x50, 0x44, 0x43, 0x41, 0x50,
      0x49, 0x48, 0x61, 0x6e, 0x64, 0x6f, 0x76, 0x65, 0x72,
      0x58, 0x20, 0x6c, 0xd6, 0x05, 0x23, 0x72, 0x0c, 0x76,
      0x4a, 0xda, 0x4b, 0xa1, 0x07, 0xe6, 0x4c, 0xcf, 0x2b,
      0xb4, 0xc8, 0x1d, 0x21, 0xa9, 0xc3, 0x02, 0x0c, 0x52,
      0xbf, 0x5c, 0x89, 0x40, 0x7d, 0x98, 0xee},
      61,
     (uint8_t *)"2025-06-10T23:59:59Z",
     kMDL,
     3321,
     { 0xa3, 0x67, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e,
      0x30, 0x69, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x81,
      0xa3, 0x67, 0x64, 0x6f, 0x63, 0x54, 0x79, 0x70, 0x65, 0x75, 0x6f, 0x72,
      0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
      0x35, 0x2e, 0x31, 0x2e, 0x6d, 0x44, 0x4c, 0x6c, 0x69, 0x73, 0x73, 0x75,
      0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xa2, 0x6a, 0x6e, 0x61,
      0x6d, 0x65, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0xa1, 0x71, 0x6f, 0x72,
      0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31, 0x33, 0x2e,
      0x35, 0x2e, 0x31, 0x81, 0xd8, 0x18, 0x58, 0x51, 0xa4, 0x68, 0x64, 0x69,
      0x67, 0x65, 0x73, 0x74, 0x49, 0x44, 0x19, 0x36, 0xf7, 0x66, 0x72, 0x61,
      0x6e, 0x64, 0x6f, 0x6d, 0x50, 0x84, 0x83, 0x3b, 0x67, 0x34, 0x97, 0x7a,
      0x63, 0x0b, 0xd7, 0xf7, 0xd4, 0xd8, 0xca, 0xd3, 0xe9, 0x71, 0x65, 0x6c,
      0x65, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
      0x69, 0x65, 0x72, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6f, 0x76, 0x65, 0x72,
      0x5f, 0x31, 0x38, 0x6c, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x56,
      0x61, 0x6c, 0x75, 0x65, 0xf4, 0x6a, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72,
      0x41, 0x75, 0x74, 0x68, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa1, 0x18, 0x21,
      0x59, 0x02, 0xce, 0x30, 0x82, 0x02, 0xca, 0x30, 0x82, 0x02, 0x2b, 0xa0,
      0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xd7, 0x05, 0x20, 0x76, 0xb8,
      0x88, 0xe1, 0x1a, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
      0x04, 0x03, 0x04, 0x30, 0x37, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
      0x04, 0x06, 0x13, 0x02, 0x5a, 0x5a, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03,
      0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31,
      0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0e, 0x50, 0x61,
      0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x52, 0x6f, 0x6f, 0x74,
      0x30, 0x1e, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x35, 0x30, 0x37, 0x30, 0x35,
      0x33, 0x34, 0x31, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x36, 0x30, 0x35, 0x30,
      0x37, 0x30, 0x35, 0x33, 0x34, 0x31, 0x30, 0x5a, 0x30, 0x42, 0x31, 0x0b,
      0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x5a, 0x5a, 0x31,
      0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f,
      0x6f, 0x67, 0x6c, 0x65, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04,
      0x03, 0x0c, 0x19, 0x50, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64,
      0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x53, 0x69,
      0x67, 0x6e, 0x65, 0x72, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
      0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
      0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xab, 0x4b, 0xb1, 0xa1, 0x9d,
      0x6b, 0xc9, 0xd6, 0x11, 0x38, 0xcf, 0x43, 0xa0, 0x04, 0xba, 0x51, 0x56,
      0xae, 0x4f, 0x30, 0x80, 0x80, 0x4c, 0x91, 0x0c, 0xf6, 0x38, 0xea, 0xcf,
      0x3b, 0x4e, 0x00, 0x21, 0x6a, 0x66, 0x70, 0x60, 0xda, 0xe4, 0x0e, 0xcc,
      0xa6, 0xfb, 0x4b, 0x39, 0xf1, 0xc9, 0x3e, 0x10, 0x98, 0xac, 0x64, 0x66,
      0x7b, 0x6e, 0x5b, 0xbf, 0x01, 0x40, 0x51, 0x86, 0x72, 0xcb, 0x7b, 0xa3,
      0x82, 0x01, 0x13, 0x30, 0x82, 0x01, 0x0f, 0x30, 0x1d, 0x06, 0x03, 0x55,
      0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x36, 0x86, 0x68, 0x1a, 0x2b, 0x1a,
      0x9c, 0xc3, 0xc5, 0x50, 0x2d, 0xe4, 0x60, 0x1d, 0x44, 0xab, 0x83, 0x81,
      0x89, 0x27, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
      0x16, 0x80, 0x14, 0x73, 0xa7, 0x04, 0x1e, 0x50, 0x81, 0x93, 0x9e, 0xe0,
      0xdb, 0xb7, 0x02, 0x8a, 0xe3, 0x71, 0x2f, 0xd0, 0xaa, 0x36, 0xc1, 0x30,
      0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
      0x02, 0x07, 0x80, 0x30, 0x21, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, 0x1a,
      0x30, 0x18, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
      0x77, 0x77, 0x77, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63,
      0x6f, 0x6d, 0x30, 0x15, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff,
      0x04, 0x0b, 0x30, 0x09, 0x06, 0x07, 0x28, 0x81, 0x8c, 0x5d, 0x05, 0x01,
      0x02, 0x30, 0x81, 0x82, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x7b, 0x30,
      0x79, 0x30, 0x77, 0xa0, 0x75, 0xa0, 0x73, 0x86, 0x71, 0x68, 0x74, 0x74,
      0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x63,
      0x61, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x36, 0x36,
      0x30, 0x33, 0x63, 0x36, 0x34, 0x37, 0x2d, 0x30, 0x30, 0x30, 0x30, 0x2d,
      0x32, 0x39, 0x64, 0x34, 0x2d, 0x61, 0x65, 0x61, 0x36, 0x2d, 0x66, 0x34,
      0x66, 0x35, 0x65, 0x38, 0x30, 0x64, 0x31, 0x62, 0x39, 0x30, 0x2e, 0x73,
      0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
      0x65, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x34, 0x31,
      0x64, 0x34, 0x33, 0x36, 0x38, 0x35, 0x39, 0x65, 0x34, 0x35, 0x64, 0x62,
      0x66, 0x33, 0x65, 0x35, 0x61, 0x39, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63,
      0x72, 0x6c, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04,
      0x03, 0x04, 0x03, 0x81, 0x8c, 0x00, 0x30, 0x81, 0x88, 0x02, 0x42, 0x00,
      0xab, 0x5b, 0x98, 0x44, 0x72, 0xb7, 0x1a, 0x25, 0x5d, 0xe8, 0x60, 0xff,
      0x9b, 0x99, 0x52, 0x8c, 0x73, 0xa9, 0xb1, 0x29, 0x7e, 0x87, 0x36, 0x57,
      0x9c, 0xb8, 0x18, 0xf6, 0x6a, 0x82, 0x5a, 0xd7, 0x0f, 0x39, 0xdd, 0x3c,
      0x18, 0x11, 0x3c, 0xf8, 0x04, 0x90, 0x5d, 0x9b, 0xa9, 0x4a, 0x03, 0xe3,
      0xfe, 0x5a, 0x9e, 0x22, 0x8f, 0x1b, 0x10, 0x1d, 0xd0, 0x86, 0x55, 0x96,
      0xe5, 0x81, 0x9c, 0xe7, 0x74, 0x02, 0x42, 0x01, 0xd0, 0x1e, 0x5d, 0x2f,
      0xc1, 0x4d, 0xb2, 0x0e, 0x4b, 0xc2, 0x0e, 0x8b, 0xaf, 0xe6, 0x1e, 0xc2,
      0x29, 0x64, 0x71, 0x31, 0xaf, 0xc1, 0xe3, 0xbb, 0xdb, 0x16, 0xd1, 0x8c,
      0x43, 0x97, 0x72, 0x11, 0xbb, 0x8f, 0xda, 0xc6, 0x6c, 0x05, 0xd1, 0x3b,
      0x76, 0xd4, 0x5b, 0x80, 0x7a, 0x20, 0x99, 0x07, 0x2b, 0x7d, 0x97, 0x86,
      0xec, 0x19, 0xcd, 0x8f, 0x4e, 0xd4, 0x85, 0x16, 0x76, 0xcf, 0x99, 0xb9,
      0xa3, 0x59, 0x08, 0x8d, 0xd8, 0x18, 0x59, 0x08, 0x88, 0xa6, 0x67, 0x76,
      0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x63, 0x31, 0x2e, 0x30, 0x6f, 0x64,
      0x69, 0x67, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74,
      0x68, 0x6d, 0x67, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x6c, 0x76,
      0x61, 0x6c, 0x75, 0x65, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0xa2,
      0x71, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30,
      0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0xb8, 0x2b, 0x19, 0x03, 0x65, 0x58,
      0x20, 0x54, 0xe2, 0xe0, 0x57, 0xf8, 0xd7, 0x41, 0x2f, 0xb9, 0x67, 0xe7,
      0x38, 0x31, 0xb1, 0xbd, 0x43, 0x9b, 0xf7, 0x61, 0x4f, 0xe3, 0x0b, 0x13,
      0xa5, 0xf5, 0xd5, 0x9d, 0x21, 0xfd, 0x8a, 0xfc, 0x0f, 0x19, 0x06, 0x4a,
      0x58, 0x20, 0x77, 0x11, 0x08, 0x33, 0x52, 0xf0, 0x6a, 0x9e, 0x71, 0x87,
      0x90, 0xb7, 0xa4, 0x2f, 0x2f, 0x88, 0xe2, 0xae, 0xee, 0xf5, 0x1e, 0x51,
      0x4f, 0xe0, 0x27, 0xde, 0x73, 0xdc, 0x60, 0x80, 0x28, 0x13, 0x19, 0x07,
      0x08, 0x58, 0x20, 0x30, 0x97, 0x45, 0x71, 0x69, 0x0f, 0x39, 0x25, 0xb5,
      0x1f, 0xa4, 0xab, 0x60, 0x2b, 0xf3, 0xcc, 0xc4, 0x44, 0x0f, 0x21, 0xd3,
      0x24, 0xe2, 0x08, 0x14, 0xea, 0xd5, 0x7a, 0xe5, 0xfb, 0x3e, 0x56, 0x19,
      0x08, 0x01, 0x58, 0x20, 0x73, 0x6a, 0x04, 0xe2, 0x35, 0x20, 0x20, 0x17,
      0xa0, 0xa1, 0x65, 0xd9, 0xd1, 0x1c, 0x0c, 0x5a, 0x74, 0xcf, 0xad, 0x39,
      0xdf, 0x94, 0x08, 0x5a, 0xb0, 0x95, 0x55, 0xc3, 0xd3, 0xfe, 0xfc, 0x4f,
      0x19, 0x13, 0x95, 0x58, 0x20, 0xe2, 0x36, 0x1c, 0x51, 0x0a, 0xd1, 0x29,
      0x42, 0xab, 0x1e, 0x5e, 0x84, 0xc4, 0x06, 0xd2, 0x4c, 0xc1, 0x7c, 0x44,
      0xaa, 0x6f, 0x79, 0x91, 0x85, 0x61, 0xf9, 0xda, 0x3e, 0xe5, 0x86, 0x80,
      0xcb, 0x19, 0x19, 0x42, 0x58, 0x20, 0x2b, 0x86, 0xfb, 0x8a, 0xd1, 0x65,
      0xce, 0x24, 0xf6, 0xb4, 0x28, 0x81, 0x91, 0x58, 0x76, 0x82, 0x19, 0x18,
      0x7a, 0x1f, 0x2a, 0x92, 0xd6, 0x5f, 0xe9, 0x42, 0x54, 0x6c, 0xae, 0xf7,
      0xe2, 0x90, 0x19, 0x24, 0x07, 0x58, 0x20, 0x97, 0x1a, 0xf5, 0x99, 0x97,
      0x59, 0x0e, 0x21, 0x54, 0xcb, 0x12, 0x32, 0xf2, 0x6d, 0x25, 0x4d, 0xcb,
      0xba, 0x57, 0x47, 0x2d, 0x17, 0x1d, 0xbb, 0x53, 0xa8, 0x24, 0x73, 0x69,
      0x46, 0x11, 0xb2, 0x19, 0x25, 0x90, 0x58, 0x20, 0x74, 0x31, 0xb9, 0xa8,
      0xe4, 0x9e, 0xa1, 0xf7, 0x7b, 0x6a, 0xf3, 0x6a, 0x2c, 0x5b, 0xe5, 0x31,
      0x97, 0x1e, 0x50, 0xc3, 0xd6, 0x80, 0xfd, 0x08, 0x0c, 0x33, 0xc0, 0x19,
      0xba, 0xfa, 0x48, 0xa9, 0x19, 0x2c, 0x0d, 0x58, 0x20, 0x7c, 0x66, 0x45,
      0x45, 0x17, 0x5e, 0xf0, 0x03, 0x70, 0xca, 0x47, 0x50, 0x8f, 0x6e, 0x19,
      0x95, 0x8f, 0x63, 0x0f, 0x2b, 0xc2, 0x92, 0xdb, 0xb4, 0x33, 0xbf, 0x73,
      0xe9, 0xec, 0xe9, 0xbb, 0xcc, 0x19, 0x2d, 0x97, 0x58, 0x20, 0x05, 0x53,
      0x0d, 0x90, 0x53, 0x30, 0xf4, 0x0b, 0x71, 0x79, 0xb6, 0x51, 0x53, 0x03,
      0x1a, 0x83, 0x01, 0xbe, 0xdd, 0x8a, 0x88, 0xc9, 0x21, 0x26, 0x77, 0xe3,
      0xd9, 0x72, 0xba, 0x05, 0x16, 0x17, 0x19, 0x2e, 0xbd, 0x58, 0x20, 0xe6,
      0x7f, 0x26, 0xac, 0x90, 0x32, 0x0a, 0xff, 0xd5, 0x1b, 0x87, 0x40, 0x58,
      0xb3, 0x55, 0xcd, 0x7c, 0x06, 0x69, 0x9c, 0x96, 0x3a, 0xb4, 0xd7, 0x2d,
      0xd7, 0xe9, 0x3d, 0x77, 0xea, 0xff, 0x3c, 0x19, 0x36, 0xe7, 0x58, 0x20,
      0x22, 0x2a, 0x50, 0xb5, 0x67, 0x8e, 0x4e, 0x06, 0x6b, 0xd4, 0x70, 0xbf,
      0xe9, 0x79, 0x91, 0x2e, 0x8a, 0xab, 0xd3, 0x1a, 0xa0, 0xc3, 0x95, 0x6c,
      0x1e, 0x41, 0xae, 0x1a, 0xe5, 0xf2, 0xe1, 0x3c, 0x19, 0x36, 0xf7, 0x58,
      0x20, 0x43, 0x9f, 0xfb, 0x14, 0x4b, 0x03, 0x08, 0xc4, 0x27, 0x23, 0xdb,
      0x01, 0xf5, 0x42, 0xff, 0x8a, 0x32, 0xd9, 0xf5, 0x49, 0x5a, 0xb2, 0xb0,
      0x7c, 0x10, 0x49, 0x20, 0xb1, 0x2a, 0xea, 0x57, 0xba, 0x19, 0x53, 0x7b,
      0x58, 0x20, 0x9c, 0xf8, 0x0d, 0xe4, 0x88, 0x8b, 0x76, 0x96, 0xfc, 0x35,
      0x9c, 0xb6, 0xa9, 0xa8, 0xf7, 0xa2, 0xe4, 0x18, 0xdd, 0xe5, 0xc9, 0xeb,
      0x4f, 0x1d, 0x56, 0x42, 0x2a, 0xa4, 0x3f, 0xc6, 0x91, 0x74, 0x19, 0x53,
      0xa0, 0x58, 0x20, 0x37, 0x43, 0x2d, 0xa3, 0xcc, 0x4f, 0x7d, 0x25, 0x20,
      0xd7, 0x57, 0x96, 0xb3, 0x54, 0x9f, 0xc0, 0x45, 0x54, 0x81, 0xaa, 0x58,
      0x01, 0xb0, 0xed, 0xc9, 0x51, 0xd5, 0x1a, 0xd6, 0x56, 0x58, 0x46, 0x19,
      0x5d, 0xf2, 0x58, 0x20, 0xd7, 0xd2, 0x86, 0xd6, 0x55, 0xf1, 0x6c, 0xef,
      0x3b, 0x2e, 0x8c, 0x22, 0xd1, 0x4c, 0xd9, 0xa3, 0x41, 0x26, 0x06, 0x58,
      0x66, 0xd9, 0x78, 0x6f, 0x4c, 0x97, 0xe5, 0x1c, 0x8c, 0xf1, 0x7d, 0x5a,
      0x19, 0x5f, 0x12, 0x58, 0x20, 0xec, 0xbf, 0x9b, 0xf5, 0x18, 0xd0, 0xa8,
      0xc1, 0x73, 0xc6, 0x5b, 0x7f, 0x6a, 0x14, 0xe5, 0x6f, 0x9e, 0x9e, 0x48,
      0x3e, 0x6c, 0x1a, 0xaa, 0x60, 0xd9, 0x30, 0xde, 0x57, 0xae, 0x5d, 0x80,
      0x66, 0x19, 0x66, 0xb4, 0x58, 0x20, 0xe1, 0xd7, 0x8f, 0xc9, 0xae, 0xae,
      0x63, 0x48, 0x0c, 0xc0, 0xd8, 0x94, 0x00, 0xa4, 0xc6, 0xe0, 0xd8, 0xb7,
      0xc5, 0xdf, 0x79, 0xcb, 0x4d, 0x67, 0x11, 0xfb, 0xb3, 0xbe, 0x7a, 0xef,
      0xd8, 0x45, 0x19, 0x67, 0x4e, 0x58, 0x20, 0x9c, 0x66, 0xa2, 0x1f, 0xdc,
      0x13, 0xfa, 0x40, 0xf6, 0xf3, 0x82, 0xc3, 0x7f, 0x53, 0x7f, 0x1c, 0x52,
      0x0b, 0xca, 0x4b, 0x09, 0x25, 0xfa, 0xdd, 0xda, 0x7b, 0x50, 0xc5, 0xb0,
      0xd9, 0x60, 0xfd, 0x19, 0x6f, 0x91, 0x58, 0x20, 0xa0, 0x91, 0x53, 0xe0,
      0x04, 0x9d, 0x18, 0x47, 0x14, 0xf8, 0x43, 0x79, 0x8c, 0x90, 0xde, 0x41,
      0xd1, 0x46, 0x5b, 0x77, 0x2f, 0x63, 0x87, 0xf3, 0xeb, 0x0e, 0x70, 0x63,
      0x81, 0xe4, 0x0a, 0x1d, 0x19, 0x71, 0x34, 0x58, 0x20, 0x31, 0xaf, 0xc9,
      0x92, 0xfe, 0xc3, 0x09, 0x98, 0xb3, 0xe8, 0x18, 0xf1, 0x45, 0xe9, 0xdd,
      0xdb, 0xf3, 0x27, 0x44, 0x4b, 0xc9, 0xaa, 0x25, 0x08, 0x2a, 0xe1, 0x85,
      0xc4, 0xfb, 0x6f, 0x18, 0xd4, 0x19, 0x72, 0x3f, 0x58, 0x20, 0xf2, 0xa3,
      0x12, 0xca, 0xdd, 0x1f, 0xf2, 0x21, 0xce, 0xdb, 0x5d, 0x86, 0x1d, 0x79,
      0xa7, 0x0c, 0x88, 0x7b, 0xdc, 0x8c, 0xd7, 0x7e, 0x3f, 0xf6, 0x36, 0xa0,
      0x12, 0xa9, 0xc1, 0x76, 0x82, 0x59, 0x19, 0x73, 0x28, 0x58, 0x20, 0xca,
      0xc8, 0x13, 0x4b, 0x17, 0x19, 0xa7, 0x4e, 0xb4, 0x8c, 0x36, 0x9a, 0x65,
      0x4d, 0xfc, 0x3a, 0x72, 0xca, 0x17, 0x3a, 0x55, 0xe0, 0x20, 0x30, 0x77,
      0xd5, 0x81, 0x21, 0xb6, 0x00, 0x30, 0x41, 0x19, 0x77, 0x39, 0x58, 0x20,
      0x44, 0x95, 0xaf, 0xb6, 0x80, 0x07, 0xab, 0xc3, 0x21, 0x66, 0xe9, 0xf2,
      0xc8, 0xfa, 0xfb, 0xd8, 0x18, 0x04, 0xac, 0x9d, 0x48, 0xb2, 0xc6, 0xe6,
      0x19, 0x0b, 0xb5, 0x2d, 0x94, 0x23, 0x8b, 0x07, 0x19, 0x7c, 0x2b, 0x58,
      0x20, 0x17, 0x1c, 0xa0, 0x43, 0x36, 0x04, 0x23, 0x86, 0x65, 0x71, 0x14,
      0x7e, 0xa2, 0x99, 0x8b, 0xa9, 0xff, 0x40, 0xde, 0xb9, 0x84, 0x84, 0x53,
      0x1c, 0xf8, 0x6f, 0xc9, 0x79, 0x1d, 0x84, 0xcb, 0x28, 0x19, 0x93, 0x07,
      0x58, 0x20, 0x3b, 0xd0, 0x95, 0x2e, 0xd7, 0x7a, 0x7d, 0x87, 0xe0, 0x3d,
      0x52, 0x0c, 0x9c, 0x5e, 0xb3, 0x27, 0x84, 0x55, 0x62, 0x48, 0xb2, 0x2a,
      0x47, 0x9c, 0xd9, 0x44, 0xcb, 0xb1, 0x64, 0x92, 0x00, 0x76, 0x19, 0x98,
      0xfa, 0x58, 0x20, 0x6e, 0x26, 0x5f, 0x85, 0x05, 0x25, 0x18, 0xb9, 0xeb,
      0xb7, 0x99, 0x32, 0x8a, 0x34, 0xcc, 0x66, 0x4a, 0xdb, 0x57, 0xef, 0x03,
      0xa2, 0x16, 0x56, 0x78, 0x0d, 0x48, 0xde, 0xc0, 0x27, 0xa5, 0x9c, 0x19,
      0x99, 0x44, 0x58, 0x20, 0x6c, 0xdd, 0xac, 0x1d, 0xd1, 0xad, 0x04, 0xbf,
      0xef, 0x67, 0x17, 0x3e, 0x88, 0x0f, 0x59, 0xb8, 0xbd, 0x2c, 0x0f, 0xb7,
      0x5a, 0xd8, 0xc5, 0xb1, 0x5c, 0xa7, 0x13, 0xd8, 0x04, 0x54, 0x2a, 0xce,
      0x19, 0xa5, 0x87, 0x58, 0x20, 0xf0, 0xf8, 0x36, 0x0f, 0xef, 0xc5, 0x73,
      0x98, 0xfc, 0xde, 0x1d, 0xa3, 0x49, 0x93, 0xc4, 0x95, 0x48, 0x28, 0xa9,
      0xda, 0x86, 0x2f, 0x4f, 0xf8, 0xe1, 0xc7, 0xee, 0x8a, 0xbc, 0x4a, 0x62,
      0xbc, 0x19, 0xab, 0xab, 0x58, 0x20, 0x09, 0xc2, 0x04, 0xc8, 0x6f, 0x93,
      0xd0, 0x11, 0xa6, 0x61, 0x8c, 0x17, 0xbc, 0xb2, 0x29, 0xf5, 0x7d, 0x93,
      0x0f, 0x41, 0x21, 0x7f, 0x2e, 0x8c, 0x27, 0xc6, 0xcb, 0xb8, 0xc1, 0x1d,
      0xd6, 0xc0, 0x19, 0xb5, 0x76, 0x58, 0x20, 0xd8, 0x5f, 0x64, 0x48, 0x3d,
      0x68, 0xe7, 0xbb, 0xca, 0x46, 0xf3, 0x69, 0x2b, 0x91, 0xb8, 0x96, 0xff,
      0x9a, 0xb1, 0xe2, 0x32, 0xf3, 0x6b, 0x4e, 0xb8, 0x65, 0x76, 0x7e, 0x31,
      0x9b, 0x3c, 0x31, 0x19, 0xba, 0xd5, 0x58, 0x20, 0x67, 0x16, 0x9a, 0x66,
      0x8c, 0x34, 0xfa, 0xec, 0x6e, 0xbc, 0x4a, 0xbd, 0xdc, 0xce, 0xc6, 0x31,
      0x30, 0x9d, 0x59, 0x1d, 0x86, 0xaa, 0xc3, 0x69, 0x74, 0x43, 0x26, 0xa0,
      0x37, 0x71, 0xae, 0xd1, 0x19, 0xbb, 0x63, 0x58, 0x20, 0xe6, 0xf9, 0xc6,
      0x68, 0x49, 0xda, 0xc8, 0xd5, 0x79, 0x9f, 0x3d, 0x7c, 0x98, 0xaa, 0xc0,
      0x8f, 0x28, 0xe6, 0x83, 0x6a, 0x47, 0x5b, 0x2e, 0xc0, 0x2a, 0x91, 0x2b,
      0x0f, 0x1a, 0x4c, 0x04, 0x27, 0x19, 0xbc, 0xc5, 0x58, 0x20, 0xe2, 0xff,
      0x42, 0x08, 0x69, 0xff, 0x52, 0x39, 0x19, 0x00, 0x4f, 0x95, 0x45, 0x0f,
      0x80, 0x32, 0x0f, 0x1b, 0x11, 0xbd, 0x9e, 0x39, 0x80, 0xa3, 0x86, 0xbf,
      0xd5, 0xc8, 0x5b, 0xc7, 0x86, 0x65, 0x19, 0xc0, 0x19, 0x58, 0x20, 0x5d,
      0x77, 0x76, 0x84, 0x99, 0x59, 0x1b, 0x05, 0x9f, 0xad, 0x15, 0xc8, 0xb7,
      0x3e, 0x77, 0x96, 0x98, 0x85, 0x4e, 0x7e, 0xe9, 0x02, 0x0c, 0x37, 0xa4,
      0xd7, 0xee, 0x84, 0x9b, 0xb8, 0xca, 0x3b, 0x19, 0xc0, 0x8c, 0x58, 0x20,
      0xf0, 0x5f, 0xd5, 0xc0, 0x4e, 0xfa, 0x49, 0x4c, 0x7d, 0xd1, 0xa9, 0xb5,
      0x5b, 0x7e, 0xdc, 0xd8, 0xee, 0xd7, 0x7a, 0x22, 0x8f, 0x32, 0x17, 0x05,
      0x41, 0xcf, 0x95, 0x82, 0x11, 0xb2, 0x36, 0x81, 0x19, 0xc5, 0xf1, 0x58,
      0x20, 0x0b, 0x4b, 0x67, 0xdf, 0xf8, 0x16, 0xad, 0x56, 0x46, 0xab, 0xc3,
      0x80, 0x74, 0x02, 0x20, 0x85, 0xd2, 0x1d, 0xde, 0xb3, 0x87, 0x95, 0xc2,
      0xd6, 0x4c, 0xe3, 0x79, 0x29, 0xf0, 0x29, 0x96, 0x2b, 0x19, 0xd9, 0x71,
      0x58, 0x20, 0x3c, 0x01, 0xc3, 0x49, 0xa4, 0x52, 0xb2, 0xb0, 0x07, 0x0f,
      0xb5, 0xac, 0xe9, 0x91, 0xb8, 0x30, 0xc1, 0x06, 0xa2, 0xce, 0x7d, 0x90,
      0xa1, 0x87, 0x56, 0x50, 0x4f, 0x46, 0x0c, 0xde, 0x29, 0x05, 0x19, 0xdd,
      0x74, 0x58, 0x20, 0xea, 0xcd, 0x7e, 0x83, 0x23, 0xe5, 0x77, 0x5e, 0x65,
      0xcf, 0x04, 0xa3, 0x2f, 0x15, 0xbf, 0xf3, 0x00, 0x74, 0x2f, 0xf4, 0xf9,
      0x43, 0xb1, 0xdc, 0xe7, 0x9f, 0xe8, 0xc2, 0x18, 0xda, 0x66, 0x1e, 0x19,
      0xe1, 0x50, 0x58, 0x20, 0x89, 0x8a, 0xfc, 0xc9, 0x16, 0xdb, 0x1f, 0x4f,
      0x53, 0x72, 0x82, 0x2c, 0x8b, 0x73, 0x9f, 0x80, 0xcd, 0xa8, 0x6f, 0x34,
      0xad, 0x89, 0xf4, 0xe0, 0x54, 0x9e, 0x0e, 0x97, 0x73, 0x9c, 0xc4, 0x01,
      0x19, 0xe7, 0x28, 0x58, 0x20, 0x22, 0x9f, 0xbb, 0x28, 0x46, 0xef, 0xd4,
      0x2a, 0x6d, 0xe3, 0x21, 0xda, 0x89, 0x53, 0x29, 0xee, 0x3f, 0x5f, 0x02,
      0xd3, 0x9a, 0x60, 0x4c, 0x4c, 0x8d, 0x6f, 0xce, 0xd5, 0xef, 0xcc, 0x11,
      0x86, 0x19, 0xf4, 0xbd, 0x58, 0x20, 0x10, 0xe4, 0x43, 0x85, 0x10, 0x27,
      0x62, 0xef, 0x6b, 0xbf, 0x0d, 0x2c, 0xee, 0xfd, 0x37, 0xdf, 0xc0, 0xb1,
      0xbe, 0x6d, 0x73, 0x07, 0xb5, 0x93, 0x0d, 0x08, 0x05, 0x61, 0xca, 0xf6,
      0x1a, 0xdf, 0x19, 0xf8, 0x18, 0x58, 0x20, 0xd3, 0xf1, 0x37, 0x31, 0x07,
      0x09, 0xe6, 0x94, 0xc6, 0x26, 0x0e, 0xd3, 0x7b, 0x90, 0x7b, 0x26, 0x96,
      0xdd, 0x44, 0x9d, 0x88, 0xff, 0x8f, 0x05, 0xba, 0x0b, 0xed, 0x56, 0xc4,
      0x3e, 0x30, 0xab, 0x77, 0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e,
      0x31, 0x38, 0x30, 0x31, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0x2e, 0x61, 0x61,
      0x6d, 0x76, 0x61, 0xa7, 0x19, 0x33, 0xd9, 0x58, 0x20, 0x86, 0x34, 0xab,
      0x2a, 0xce, 0xa2, 0x32, 0x3a, 0x4c, 0x7c, 0xce, 0x45, 0x33, 0x93, 0x9a,
      0x3f, 0xb3, 0x4d, 0x41, 0xca, 0x68, 0x0e, 0x6b, 0x8e, 0x27, 0x16, 0x58,
      0xf0, 0x9e, 0x31, 0x65, 0x77, 0x19, 0x35, 0xb6, 0x58, 0x20, 0xd8, 0xb8,
      0x74, 0x4b, 0x9d, 0x7c, 0xa9, 0xc6, 0x89, 0x4f, 0x0d, 0x5d, 0x2f, 0x7d,
      0x1e, 0x54, 0x97, 0x5f, 0x3b, 0xdc, 0x9b, 0xfb, 0x4b, 0xe1, 0xb5, 0x34,
      0x8e, 0xc5, 0x38, 0xe3, 0xb7, 0x03, 0x19, 0x50, 0x2f, 0x58, 0x20, 0xd0,
      0x1c, 0xe7, 0x38, 0xf7, 0x1c, 0x26, 0x0b, 0x33, 0x6a, 0x3c, 0x5d, 0x79,
      0x77, 0xdf, 0x5d, 0x1b, 0x42, 0xad, 0x22, 0x54, 0xcc, 0x32, 0x11, 0xde,
      0x92, 0x95, 0x0c, 0x35, 0x46, 0xee, 0x3d, 0x19, 0x92, 0x51, 0x58, 0x20,
      0x88, 0x8f, 0xc9, 0x85, 0xcb, 0xee, 0x8e, 0xda, 0x02, 0x8e, 0x70, 0xb2,
      0x3d, 0x2b, 0xfc, 0x6d, 0x99, 0x72, 0x3e, 0x4e, 0xa2, 0x77, 0xca, 0x3b,
      0x91, 0xbc, 0x02, 0xec, 0x18, 0x20, 0x0a, 0x6f, 0x19, 0xa4, 0x7c, 0x58,
      0x20, 0xb3, 0x75, 0x9c, 0xee, 0x1e, 0x90, 0xc0, 0x59, 0x09, 0xe3, 0x1f,
      0x69, 0x53, 0xf2, 0x08, 0x5f, 0x3e, 0xa6, 0xa0, 0xf8, 0x4d, 0x36, 0x89,
      0x6a, 0xd1, 0x56, 0xb4, 0x66, 0x24, 0x84, 0x49, 0x18, 0x19, 0xb6, 0x49,
      0x58, 0x20, 0x1b, 0xdd, 0xe3, 0x1d, 0x45, 0x82, 0x65, 0xe6, 0x59, 0xa6,
      0xf9, 0x7c, 0x34, 0xd2, 0x76, 0xa6, 0xff, 0x6a, 0x4f, 0x95, 0xdc, 0xbf,
      0x8b, 0x31, 0xa5, 0x50, 0x2d, 0x5e, 0x09, 0x65, 0xac, 0x05, 0x19, 0xe4,
      0xe4, 0x58, 0x20, 0x27, 0x9e, 0x2e, 0x80, 0x83, 0xf8, 0x7f, 0x13, 0x43,
      0xae, 0x43, 0x98, 0x92, 0x26, 0x0a, 0xf6, 0xc7, 0x39, 0x28, 0x69, 0x66,
      0xb8, 0xdb, 0x15, 0xbe, 0x75, 0x1d, 0xe2, 0x45, 0xe5, 0x42, 0x0c, 0x6d,
      0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x49, 0x6e, 0x66,
      0x6f, 0xa1, 0x69, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79,
      0xa4, 0x01, 0x02, 0x20, 0x01, 0x21, 0x58, 0x20, 0x0b, 0xaa, 0x22, 0x38,
      0x68, 0x47, 0x64, 0x27, 0x74, 0x33, 0xe4, 0x35, 0x17, 0xd0, 0x8f, 0x35,
      0xa4, 0xbd, 0x72, 0xb2, 0x3b, 0xce, 0xf0, 0x28, 0x75, 0xae, 0x51, 0x0b,
      0x88, 0x85, 0x99, 0xca, 0x22, 0x58, 0x20, 0xa5, 0xf5, 0x7b, 0xc8, 0x56,
      0xe3, 0x9e, 0xa3, 0x8f, 0x18, 0xc7, 0x91, 0xb8, 0xca, 0x7c, 0xc3, 0x9b,
      0xaf, 0xec, 0x37, 0x1b, 0xe0, 0x29, 0xa0, 0x28, 0x49, 0xa9, 0xb7, 0xfb,
      0x0c, 0x31, 0x04, 0x67, 0x64, 0x6f, 0x63, 0x54, 0x79, 0x70, 0x65, 0x75,
      0x6f, 0x72, 0x67, 0x2e, 0x69, 0x73, 0x6f, 0x2e, 0x31, 0x38, 0x30, 0x31,
      0x33, 0x2e, 0x35, 0x2e, 0x31, 0x2e, 0x6d, 0x44, 0x4c, 0x6c, 0x76, 0x61,
      0x6c, 0x69, 0x64, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0xa3, 0x66,
      0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0xc0, 0x74, 0x32, 0x30, 0x32, 0x35,
      0x2d, 0x30, 0x36, 0x2d, 0x30, 0x35, 0x54, 0x31, 0x36, 0x3a, 0x35, 0x35,
      0x3a, 0x31, 0x31, 0x5a, 0x69, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x46, 0x72,
      0x6f, 0x6d, 0xc0, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2d, 0x30, 0x36, 0x2d,
      0x30, 0x35, 0x54, 0x31, 0x36, 0x3a, 0x35, 0x35, 0x3a, 0x31, 0x31, 0x5a,
      0x6a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0xc0,
      0x74, 0x32, 0x30, 0x32, 0x35, 0x2d, 0x30, 0x36, 0x2d, 0x31, 0x39, 0x54,
      0x31, 0x36, 0x3a, 0x35, 0x35, 0x3a, 0x31, 0x31, 0x5a, 0x58, 0x40, 0x35,
      0x49, 0x2b, 0x30, 0x4e, 0xe2, 0x80, 0x8e, 0xd6, 0x93, 0x9d, 0xf8, 0xe2,
      0xb2, 0x65, 0x01, 0xee, 0xe2, 0x89, 0xd3, 0x2b, 0x0b, 0x25, 0x96, 0x46,
      0xe4, 0x89, 0x9d, 0xbe, 0x87, 0xb2, 0xf4, 0xfc, 0xe6, 0x90, 0x9c, 0xeb,
      0x2c, 0x1c, 0x5c, 0x8e, 0x81, 0x3d, 0x8d, 0x28, 0xd5, 0x02, 0x47, 0x99,
      0x72, 0x6e, 0x3c, 0x8e, 0x50, 0x26, 0xeb, 0x67, 0xe5, 0xc3, 0xfe, 0xfe,
      0x31, 0x92, 0x30, 0x6c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x69,
      0x67, 0x6e, 0x65, 0x64, 0xa2, 0x6a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x70,
      0x61, 0x63, 0x65, 0x73, 0xd8, 0x18, 0x41, 0xa0, 0x6a, 0x64, 0x65, 0x76,
      0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0xa1, 0x6f, 0x64, 0x65, 0x76,
      0x69, 0x63, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
      0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0xf6, 0x58, 0x40, 0x92, 0x37, 0x26,
      0x8c, 0xe3, 0x53, 0xc8, 0x55, 0x11, 0xe7, 0x50, 0x58, 0x75, 0x3d, 0xa2,
      0x03, 0x8b, 0xca, 0x9d, 0x20, 0xf7, 0x59, 0x51, 0x96, 0x1f, 0x14, 0xf7,
      0x16, 0xc5, 0x9f, 0xef, 0x24, 0xd2, 0x45, 0xdb, 0xef, 0x45, 0x87, 0x02,
      0x9f, 0x88, 0x5a, 0xed, 0x8d, 0xce, 0xba, 0x1a, 0xf8, 0x30, 0xa4, 0x49,
      0x3b, 0xbc, 0xfa, 0xe6, 0x0b, 0xc2, 0xa5, 0xff, 0x99, 0xa1, 0xc1, 0x85,
      0x07, 0x66, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x00}},
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_EXAMPLES_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_CONSTANTS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_CONSTANTS_H_

#include <cstddef>
namespace proofs {

static constexpr size_t kSHAJWTPluckerBits = 4u;
static constexpr size_t kMaxJWTSHABlocks = 7;
static constexpr size_t kJWTIndexBits = 10; /* #bits to index into payload */

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_JWT_JWT_CONSTANTS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_FLATSHA256_WITNESS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_FLATSHA256_WITNESS_H_

#include <stddef.h>
#include <stdint.h>

namespace proofs {

uint32_t SHA256_ru32be(const uint8_t *d);

class FlatSHA256Witness {
 public:
  struct BlockWitness {
    uint32_t outw[48];
    uint32_t oute[64];
    uint32_t outa[64];
    uint32_t h1[8];
  };

  static void transform_and_witness_block(const uint32_t in[16],
                                          const uint32_t H0[8],
                                          uint32_t outw[48], uint32_t oute[64],
                                          uint32_t outa[64], uint32_t H1[8]);

  static void transform_and_witness_message(size_t n, const uint8_t msg[/*n*/],
                                            size_t max, uint8_t &numb,
                                            uint8_t in[/* 64*max */],
                                            BlockWitness bw[/*max*/]);
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_FLATSHA256_WITNESS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_HASH_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_HASH_H_

#include <cstddef>
#include <cstdint>

#include "util/crc64.h"

namespace proofs {

// canonical hash of an Elt
template <class Field>
uint64_t elt_hash(const typename Field::Elt& k, const Field& F) {
  uint64_t crc = 0x1;
  uint8_t buf[Field::kBytes];
  F.to_bytes_field(buf, k);
  for (size_t l = 0; l < Field::kBytes; ++l) {
    crc = crc64::update(crc, buf[l], 8);
  }
  return crc;
}

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_HASH_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_SUMCHECK_PROVER_H_
#define PRIVACY_PROOFS_ZK_LIB_SUMCHECK_PROVER_H_

#include <stddef.h>

#include "random/transcript.h"
#include "sumcheck/circuit.h"
#include "sumcheck/prover_layers.h"
#include "sumcheck/transcript_sumcheck.h"

namespace proofs {

// A high level idea is partially described in chapter 4.6.7 "Leveraging Data
// Parallelism for Further Speedups" in the book "Proofs, Arguments, and
// Zero-Knowledge" by Justin Thaler.
template <class Field>
class Prover : public ProverLayers<Field> {
  using super = ProverLayers<Field>;
  using typename super::bindings;

 public:
  using typename super::inputs;

  explicit Prover(const Field& f) : ProverLayers<Field>(f) {}

  // Generate proof for circuit. pad can be nullptr if the caller does not
  // want to add any pad to the proof. Caller must ensure in, t, and F remain
  // valid during call duration.
  // This method always succeeds, but may not produce a verifying proof if
  // the inputs do not satisfy the circuit.
  void prove(Proof<Field>* proof, const Proof<Field>* pad,
             const Circuit<Field>* circ, const inputs& in, Transcript& t) {
    if (proof == nullptr || circ == nullptr) return;

    TranscriptSumcheck<Field> ts(t, super::f_);
    // The input X is stored at in's layer nl - 1.
    ts.write_input(in.at(circ->nl - 1).get());
    bindings bnd;
    super::prove(proof, pad, circ, in, /*aux=*/nullptr, bnd, ts, super::f_);
  }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_SUMCHECK_PROVER_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA3_SHA3_REFERENCE_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA3_SHA3_REFERENCE_H_

// !!!!! DO NOT USE IN PRODUCTION !!!!!

/* This is a simple reference implementation of sha3
   to be used to design zero-knowledge circuits.  DO NOT USE
   THIS CODE IN PRODUCTION. */
#include <cstdint>
#include <cstdlib>

namespace proofs {
class Sha3Reference {
  size_t mdlen_;
  size_t rate_;
  size_t wrptr_;
  uint8_t buf_[200];
  uint64_t a_[5][5];

  static void keccak_f_1600(uint64_t A[5][5]);

 public:
  explicit Sha3Reference(size_t mdlen)
      : mdlen_(mdlen), rate_(200 - 2 * mdlen), wrptr_(0), buf_{}, a_{} {}

  void update(const char* data, size_t n);
  void final(uint8_t digest[/*mdlen*/]);

  static void keccak_f_1600_DEBUG_ONLY(uint64_t A[5][5]);
};
}  // namespace proofs
#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA3_SHA3_REFERENCE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_CONSTANTS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_CONSTANTS_H_

#include <stddef.h>

namespace proofs {
struct CborConstants {
  static constexpr size_t kNCounters = 4;
  static constexpr size_t kIndexBits = 12;
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_CBOR_PARSER_CBOR_CONSTANTS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_COMPARE_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_COMPARE_H_

#include <cstddef>
#include <cstdint>

namespace proofs {

// canonical a < b operation, defined as lexicographic comparison of
// the Elt's serialization
template <class Field>
bool elt_less_than(const typename Field::Elt& a, const typename Field::Elt& b,
                   const Field& F) {
  uint8_t ua[Field::kBytes], ub[Field::kBytes];
  F.to_bytes_field(ua, a);
  F.to_bytes_field(ub, b);
  for (size_t j = 0; j < Field::kBytes; ++j) {
    if (ua[j] < ub[j]) return true;
    if (ua[j] > ub[j]) return false;
  }
  return false;  // equal
}

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_COMPARE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_EC_P256_H_
#define PRIVACY_PROOFS_ZK_LIB_EC_P256_H_

/*
This file declares the one instance of the P256 curve and its related fields.
There should be only one instance of this curve in any program due to the
typing conventions.

This curve is also known as secp256r1 and prime256v1.

It is defined over the base field F_p for
p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff
= 115792089210356248762697446949407573530086143415290314195533631308867097853951

and has an order of
0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
115792089210356248762697446949407573529996955224135760342422259061068512044369

*/

#include "algebra/fp.h"
#include "algebra/fp_p256.h"
#include "ec/elliptic_curve.h"

namespace proofs {

using Fp256Base = Fp256<true>;
using Fp256Scalar = Fp<4, true>;
using Fp256Nat = Fp256Base::N;

// This is the base field of the curve.
extern const Fp256Base p256_base;

// Order of the curve.
extern const Fp256Nat n256_order;

// This field allows operations mod the order of the curve.
extern const Fp256Scalar p256_scalar;

typedef EllipticCurve<Fp256Base, 4, 256> P256;

extern const P256 p256;
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_EC_P256_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_CONSTANTS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_CONSTANTS_H_

#include <stddef.h>
#include <stdint.h>

namespace proofs {
// bit-plucker code common to both compiler-time and
// wire-fill time
template <class Field, size_t N>
struct bit_plucker_point {
  using Elt = typename Field::Elt;

  // packing of bits compatible with even_lagrange_basis():
  Elt operator()(uint64_t bits, const Field& F) const {
    return F.subf(F.of_scalar(2 * bits), F.of_scalar(N - 1));
  }
};
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_CONSTANTS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_UTIL_SERIALIZATION_H_
#define PRIVACY_PROOFS_ZK_LIB_UTIL_SERIALIZATION_H_

#include <cstdint>

namespace proofs {

static inline void u64_to_le(uint8_t a[/*8*/], uint64_t x) {
  a[0] = x & 0xffu;
  a[1] = (x >> 8) & 0xffu;
  a[2] = (x >> 16) & 0xffu;
  a[3] = (x >> 24) & 0xffu;
  a[4] = (x >> 32) & 0xffu;
  a[5] = (x >> 40) & 0xffu;
  a[6] = (x >> 48) & 0xffu;
  a[7] = (x >> 56) & 0xffu;
}

static inline uint64_t u64_of_le(const uint8_t a[/*8*/]) {
  return ((uint64_t)a[7] << 56) | ((uint64_t)a[6] << 48) |
         ((uint64_t)a[5] << 40) | ((uint64_t)a[4] << 32) |
         ((uint64_t)a[3] << 24) | ((uint64_t)a[2] << 16) |
         ((uint64_t)a[1] << 8) | (uint64_t)a[0];
}

static inline void u32_to_le(uint8_t a[/*4*/], uint32_t x) {
  a[0] = x & 0xffu;
  a[1] = (x >> 8) & 0xffu;
  a[2] = (x >> 16) & 0xffu;
  a[3] = (x >> 24) & 0xffu;
}

static inline uint32_t u32_of_le(const uint8_t a[/*4*/]) {
  return ((uint32_t)a[3] << 24) | ((uint32_t)a[2] << 16) |
         ((uint32_t)a[1] << 8) | (uint32_t)a[0];
}

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_UTIL_SERIALIZATION_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_REVOCATION_CONSTANTS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_REVOCATION_CONSTANTS_H_

#include <cstddef>
namespace proofs {

static constexpr size_t kSHARevocationPluckerBits = 4u;

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_MDOC_MDOC_REVOCATION_CONSTANTS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ARRAYS_AFFINE_H_
#define PRIVACY_PROOFS_ZK_LIB_ARRAYS_AFFINE_H_

#include <stddef.h>

namespace proofs {

using corner_t = size_t;

// return r * f0 + (1-r) * f1 = f0 + r * (f1 - f0)
template <typename Field>
typename Field::Elt affine_interpolation(const typename Field::Elt& r,
                                         typename Field::Elt f0,
                                         typename Field::Elt f1,
                                         const Field& F) {
  F.sub(f1, f0);
  F.mul(f1, r);
  F.add(f0, f1);
  return f0;
}

// special case f0 = 0
template <typename Field>
typename Field::Elt affine_interpolation_z_nz(const typename Field::Elt& r,
                                              typename Field::Elt f1,
                                              const Field& F) {
  F.mul(f1, r);
  return f1;
}

// special case f1 = 0
template <typename Field>
typename Field::Elt affine_interpolation_nz_z(const typename Field::Elt& r,
                                              typename Field::Elt f0,
                                              const Field& F) {
  F.sub(f0, F.mulf(f0, r));
  return f0;
}

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_ARRAYS_AFFINE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_UTIL_CRC64_H_
#define PRIVACY_PROOFS_ZK_LIB_UTIL_CRC64_H_

/*
This package defines 3 basic methods for computing a simple 64-bit CRC.
It is used for checksum and comparison of datastructures that are internal
to this library.
*/

#include <stdlib.h>

#include <cstdint>

namespace proofs {
namespace crc64 {
static inline uint64_t shlu64(uint64_t x, size_t n) {
  return (n >= 64) ? 0u : (x << n);
}
static inline uint64_t shru64(uint64_t x, size_t n) {
  return (n >= 64) ? 0u : (x >> n);
}
static inline uint64_t update(uint64_t crc, uint64_t u, size_t n = 64) {
  crc ^= u;
  uint64_t l = shlu64(crc, 127u - n) ^ shlu64(crc, 125u - n) ^
               shlu64(crc, 124u - n) ^ shlu64(crc, 64u - n);
  return shru64(crc, n) ^ l ^ (l >> 1) ^ (l >> 3) ^ (l >> 4);
}
}  // namespace crc64
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_UTIL_CRC64_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_UTIL_PANIC_H_
#define PRIVACY_PROOFS_ZK_LIB_UTIL_PANIC_H_

#if defined(__ABSL__)
#include "third_party/absl/log/check.h"
#else
#include <cstdio>
#include <cstdlib>
#endif

namespace proofs {

inline void check(bool truth, const char* why) {
#if defined(__ABSL__)
  CHECK(truth) << why;
#else
  if (!truth) {
    fprintf(stderr, "%s", why);
    abort();
  }
#endif
}

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_UTIL_PANIC_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_SHA256_CONSTANTS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_SHA256_CONSTANTS_H_

#include <cstdint>

namespace proofs {

// Array of round constants used to define SHA256.
// See FIPS 180-4, section 4.2.2.
extern const uint32_t kSha256Round[64];

}  // namespace proofs
#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_SHA256_CONSTANTS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_BASE64_DECODE_UTIL_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_BASE64_DECODE_UTIL_H_

#include <stdint.h>

#include <string>
#include <vector>

namespace proofs {

bool base64_decode_url(std::string inp, std::vector<uint8_t>& out);

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_BASE64_DECODE_UTIL_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA3_SHA3_ROUND_CONSTANTS_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA3_SHA3_ROUND_CONSTANTS_H_

#include <cstdint>
#include <cstdlib>

namespace proofs {
extern const uint64_t sha3_rc[24];
extern const size_t sha3_rotc[24];
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA3_SHA3_ROUND_CONSTANTS_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_RANDOM_SECURE_RANDOM_ENGINE_H_
#define PRIVACY_PROOFS_ZK_LIB_RANDOM_SECURE_RANDOM_ENGINE_H_

#include <stdlib.h>

#include <cstddef>
#include <cstdint>

#include "random/random.h"
#include "util/crypto.h"

namespace proofs {

// SecureRandomEngine is a RandomEngine that uses openssl.
class SecureRandomEngine : public RandomEngine {
 public:
  SecureRandomEngine() = default;
  void bytes(uint8_t* buf, size_t n) override { rand_bytes(buf, n); }
};

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_RANDOM_SECURE_RANDOM_ENGINE_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_FLATSHA256_IO_H_
#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_FLATSHA256_IO_H_

#include <stddef.h>

namespace proofs {

constexpr const size_t kShaPluckerSize = 2;

}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_SHA_FLATSHA256_IO_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_STATIC_STRING_H_
#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_STATIC_STRING_H_

#include <cstddef>

// utility class to wrap pointers to compile-time
// constant strings
struct StaticString {
  template <size_t N>
  explicit StaticString(const char (&s)[N]) : as_pointer(s) {}

  const char* as_pointer;
};

#endif  // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_STATIC_STRING_H_

// Copyright 2025 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PRIVACY_PROOFS_ZK_LIB_UTIL_LOG_H_
#define PRIVACY_PROOFS_ZK_LIB_UTIL_LOG_H_

// Simple, self-contained logger for this library.

namespace proofs {

enum LogLevel {
  ERROR = 1,
  WARNING = 10,
  INFO = 100,
};

void set_log_level(enum LogLevel l);

void log(enum LogLevel l, const char* format, ...);
}  // namespace proofs

#endif  // PRIVACY_PROOFS_ZK_LIB_UTIL_LOG_H_

class
mdoc_1f
{
const LogicCircuit& lc_;
const EC& ec_;
const Nat& order_;
public:
struct CborIndex {
    vind k, v, ndx;
    void input(const LogicCircuit& lc) {
      k = lc.template vinput<kMdoc1CborIndexBits>();
      v = lc.template vinput<kMdoc1CborIndexBits>();
      ndx = lc.template vinput<kMdoc1CborIndexBits>();
    }
  };
struct AttrShift {
    vind offset;
    vind len;
    void input(const LogicCircuit& lc) {
      offset = lc.template vinput<kMdoc1CborIndexBits>();
      len = lc.template vinput<kMdoc1CborIndexBits>();
    }
  };
class Witness {
   public:
    EltW e_;
    EltW dpkx_, dpky_;

    EcdsaWitness sig_;
    EcdsaWitness dpk_sig_;

    v8 in_[64 * kMdoc1MaxSHABlocks]; /* input bytes, 64 * MAX */
    v8 nb_; /* index of sha block that contains the real hash  */
    ShaBlockWitness sig_sha_[kMdoc1MaxSHABlocks];

    size_t num_attr_;

    std::vector<std::vector<ShaBlockWitness>> attr_sha_;
    std::vector<std::vector<v8>> attrb_;

    std::vector<CborIndex> attr_mso_;
    std::vector<AttrShift> attr_ei_;
    std::vector<AttrShift> attr_ev_;

    std::vector<v8> incb_;
    std::vector<typename Cbor::position_witness> pwcb_;
    typename Cbor::global_witness gwcb_;

    vind prepad_, mso_len_;

    CborIndex valid_, valid_from_, valid_until_;
    CborIndex dev_key_info_, dev_key_, dev_key_pkx_, dev_key_pky_;
    CborIndex value_digests_, org_;

    explicit Witness(size_t num_attr)
        : num_attr_(num_attr),
          attr_sha_(num_attr),
          attrb_(num_attr),
          attr_mso_(num_attr),
          attr_ei_(num_attr),
          attr_ev_(num_attr),
          incb_(kMdoc1MaxMsoLen),
          pwcb_(kMdoc1MaxMsoLen) {
      for (size_t i = 0; i < num_attr; ++i) {
        attr_sha_[i].resize(2);
      }
    }

    void input(QuadCircuit<Field>& Q, const LogicCircuit& lc) {
      e_ = Q.input();
      dpkx_ = Q.input();
      dpky_ = Q.input();

      sig_.input(Q);
      dpk_sig_.input(Q);

      nb_ = lc.template vinput<8>();

      // sha input init (skip the prefix) =========================
      for (size_t i = 0; i + kCose1PrefixLen < 64 * kMdoc1MaxSHABlocks; ++i) {
        in_[i] = lc.template vinput<8>();
      }

      for (size_t j = 0; j < kMdoc1MaxSHABlocks; j++) {
        sig_sha_[j].input(Q);
      }

      // Cbor input init: note, the inC array will be constructed in the
      // circuit.
      prepad_ = lc.template vinput<kMdoc1CborIndexBits>();
      mso_len_ = lc.template vinput<kMdoc1CborIndexBits>();
      for (size_t i = 0; i < kMdoc1MaxMsoLen; ++i) {
        pwcb_[i].encoded_sel_header = Q.input();
      }
      gwcb_.invprod_decode = Q.input();
      gwcb_.cc0 = Q.input();
      gwcb_.invprod_parse = Q.input();

      valid_.input(lc);
      valid_from_.input(lc);
      valid_until_.input(lc);
      dev_key_info_.input(lc);
      dev_key_.input(lc);
      dev_key_pkx_.input(lc);
      dev_key_pky_.input(lc);
      value_digests_.input(lc);
      org_.input(lc);

      // Attribute opening witnesses
      for (size_t ai = 0; ai < num_attr_; ++ai) {
        for (size_t i = 0; i < 64 * 2; ++i) {
          attrb_[ai].push_back(lc.template vinput<8>());
        }
        for (size_t j = 0; j < 2; j++) {
          attr_sha_[ai][j].input(Q);
        }
        attr_mso_[ai].input(lc);
        attr_ei_[ai].input(lc);
        attr_ev_[ai].input(lc);
      }
    }
  };
struct OpenedAttribute {
    v8 attr[96];  // representing attribute name, elementValue delimiter, and
                  // finally the attribute value.
  };
struct PathEntry {
    CborIndex ind;
    size_t l;
    const uint8_t* name;
  };
explicit mdoc_1f(const LogicCircuit& lc, const EC& ec, const Nat& order) : lc_(lc), ec_(ec), order_(order), sha_(lc), r_(lc), cbor_(lc) {}
void assert_credential(EltW pkX, EltW pkY, EltW hash_tr,
                         OpenedAttribute oa[/* NUM_ATTR */],
                         const v8 now[/*kDateLen*/], const Witness& vw) const {}
private:
EltW repack32(const sha_packed_v32 H[]) const {}
void assert_hash(const EltW& e, const Witness& vw) const {}
void assert_attribute(size_t max, const vind& len, const v8 got[/*max*/],
                        const v8 want[/*max*/]) const {}
void assert_path(size_t len, PathEntry p[], const Witness& vw,
                   std::vector<typename Cbor::decode>& dsC,
                   std::vector<typename Cbor::parse_output>& psC) const {}
Flatsha sha_;
Routing r_;
Cbor cbor_;
};

This code segment functions to: Implement cryptographic verification protocols for secured digital credentials by validating signature authenticity, enforcing data structure integrity through hashing mechanisms, and ensuring compliance with standardized encoding formats during document processing.

```cpp
[TASK_BEGIN]
class mdoc_1f {
  const LogicCircuit& lc_;
  const EC& ec_;
  const Nat& order_;

 public:
  struct CborIndex {
    vind k, v, ndx;
    void input(const LogicCircuit& lc) {
      k = lc.template vinput<kMdoc1CborIndexBits>();
      v = lc.template vinput<kMdoc1CborIndexBits>();
      ndx = lc.template vinput<kMdoc1CborIndexBits>();
    }
  };

  struct AttrShift {
    vind offset;
    vind len;
    void input(const LogicCircuit& lc) {
      offset = lc.template vinput<kMdoc1CborIndexBits>();
      len = lc.template vinput<kMdoc1CborIndexBits>();
    }
  };

  class Witness {
   public:
    EltW e_;
    EltW dpkx_, dpky_;

    EcdsaWitness sig_;
    EcdsaWitness dpk_sig_;

    v8 in_[64 * kMdoc1MaxSHABlocks];
    v8 nb_;
    ShaBlockWitness sig_sha_[kMdoc1MaxSHABlocks];

    size_t num_attr_;

    std::vector<std::vector<ShaBlockWitness>> attr_sha_;
    std::vector<std::vector<v8>> attrb_;
    std::vector<CborIndex> attr_mso_;
    std::vector<AttrShift> attr_ei_;
    std::vector<AttrShift> attr_ev_;

    std::vector<v8> incb_;
    std::vector<typename Cbor::position_witness> pwcb_;
    typename Cbor::global_witness gwcb_;

    vind prepad_, mso_len_;

    CborIndex valid_, valid_from_, valid_until_;
    CborIndex dev_key_info_, dev_key_, dev_key_pkx_, dev_key_pky_;
    CborIndex value_digests_, org_;

    explicit Witness(size_t num_attr)
        : num_attr_(num_attr),
          attr_sha_(num_attr),
          attrb_(num_attr),
          attr_mso_(num_attr),
          attr_ei_(num_attr),
          attr_ev_(num_attr),
          incb_(kMdoc1MaxMsoLen),
          pwcb_(kMdoc1MaxMsoLen) {
      for (size_t i = 0; i < num_attr; ++i) {
        attr_sha_[i].resize(2);
      }
    }

    void input(QuadCircuit<Field>& Q, const LogicCircuit& lc) {
      e_ = Q.input();
      dpkx_ = Q.input();
      dpky_ = Q.input();

      sig_.input(Q);
      dpk_sig_.input(Q);

      nb_ = lc.template vinput<8>();

      for (size_t i = 0; i + kCose1PrefixLen < 64 * kMdoc1MaxSHABlocks; ++i) {
        in_[i] = lc.template vinput<8>();
      }

      for (size_t j = 0; j < kMdoc1MaxSHABlocks; j++) {
        sig_sha_[j].input(Q);
      }

      prepad_ = lc.template vinput<kMdoc1CborIndexBits>();
      mso_len_ = lc.template vinput<kMdoc1CborIndexBits>();
      for (size_t i = 0; i < kMdoc1MaxMsoLen; ++i) {
        pwcb_[i].encoded_sel_header = Q.input();
      }
      gwcb_.invprod_decode = Q.input();
      gwcb_.cc0 = Q.input();
      gwcb_.invprod_parse = Q.input();

      valid_.input(lc);
      valid_from_.input(lc);
      valid_until_.input(lc);
      dev_key_info_.input(lc);
      dev_key_.input(lc);
      dev_key_pkx_.input(lc);
      dev_key_pky_.input(lc);
      value_digests_.input(lc);
      org_.input(lc);

      for (size_t ai = 0; ai < num_attr_; ++ai) {
        for (size_t i = 0; i < 64 * 2; ++i) {
          attrb_[ai].push_back(lc.template vinput<8>());
        }
        for (size_t j = 0; j < 2; j++) {
          attr_sha_[ai][j].input(Q);
        }
        attr_mso_[ai].input(lc);
        attr_ei_[ai].input(lc);
        attr_ev_[ai].input(lc);
      }
    }
  };

  struct OpenedAttribute {
    v8 attr[96];
  };

  struct PathEntry {
    CborIndex ind;
    size_t l;
    const uint8_t* name;
  };

  explicit mdoc_1f(const LogicCircuit& lc, const EC& ec, const Nat& order)
      : lc_(lc), ec_(ec), order_(order), sha_(lc), r_(lc), cbor_(lc) {}

  void assert_credential(EltW pkX, EltW pkY, EltW hash_tr,
                         OpenedAttribute oa[/* NUM_ATTR */],
                         const v8 now[/*kDateLen*/], const Witness& vw) const {}

 private:
  EltW repack32(const sha_packed_v32 H[]) const {}
  void assert_hash(const EltW& e, const Witness& vw) const {}
  void assert_attribute(size_t max, const vind& len, const v8 got[/*max*/],
                        const v8 want[/*max*/]) const {}
  void assert_path(size_t len, PathEntry p[], const Witness& vw,
                   std::vector<typename Cbor::decode>& dsC,
                   std::vector<typename Cbor::parse_output>& psC) const {}

  Flatsha sha_;
  Routing r_;
  Cbor cbor_;
};
[TASK_END]
```

class mdoc_1f {
  const LogicCircuit& lc_;
  const EC& ec_;
  const Nat& order_;
 public:
  struct CborIndex {
    vind k, v, ndx;
    void input(const LogicCircuit& lc) {
      k = lc.template vinput<kMdoc1CborIndexBits>();
      v = lc.template vinput<kMdoc1CborIndexBits>();
      ndx = lc.template vinput<kMdoc1CborIndexBits>();
    }
  };
  struct AttrShift {
    vind offset;
    vind len;
    void input(const LogicCircuit& lc) {
      offset = lc.template vinput<kMdoc1CborIndexBits>();
      len = lc.template vinput<kMdoc1CborIndexBits>();
    }
  };
  class Witness {
   public:
    EltW e_;
    EltW dpkx_, dpky_;
    EcdsaWitness sig_;
    EcdsaWitness dpk_sig_;
    v8 in_[64 * kMdoc1MaxSHABlocks];
    v8 nb_;
    ShaBlockWitness sig_sha_[kMdoc1MaxSHABlocks];
    size_t num_attr_;
    std::vector<std::vector<ShaBlockWitness>> attr_sha_;
    std::vector<std::vector<v8>> attrb_;
    std::vector<CborIndex> attr_mso_;
    std::vector<AttrShift> attr_ei_;
    std::vector<AttrShift> attr_ev_;
    std::vector<v8> incb_;
    std::vector<typename Cbor::position_witness> pwcb_;
    typename Cbor::global_witness gwcb_;
    vind prepad_, mso_len_;
    CborIndex valid_, valid_from_, valid_until_;
    CborIndex dev_key_info_, dev_key_, dev_key_pkx_, dev_key_pky_;
    CborIndex value_digests_, org_;
    explicit Witness(size_t num_attr)
        : num_attr_(num_attr),
          attr_sha_(num_attr),
          attrb_(num_attr),
          attr_mso_(num_attr),
          attr_ei_(num_attr),
          attr_ev_(num_attr),
          incb_(kMdoc1MaxMsoLen),
          pwcb_(kMdoc1MaxMsoLen) {
      for (size_t i = 0; i < num_attr; ++i) {
        attr_sha_[i].resize(2);
      }
    }
    void input(QuadCircuit<Field>& Q, const LogicCircuit& lc) {
      e_ = Q.input();
      dpkx_ = Q.input();
      dpky_ = Q.input();
      sig_.input(Q);
      dpk_sig_.input(Q);
      nb_ = lc.template vinput<8>();
      for (size_t i = 0; i + kCose1PrefixLen < 64 * kMdoc1MaxSHABlocks; ++i) {
        in_[i] = lc.template vinput<8>();
      }
      for (size_t j = 0; j < kMdoc1MaxSHABlocks; j++) {
        sig_sha_[j].input(Q);
      }
      prepad_ = lc.template vinput<kMdoc1CborIndexBits>();
      mso_len_ = lc.template vinput<kMdoc1CborIndexBits>();
      for (size_t i = 0; i < kMdoc1MaxMsoLen; ++i) {
        pwcb_[i].encoded_sel_header = Q.input();
      }
      gwcb_.invprod_decode = Q.input();
      gwcb_.cc0 = Q.input();
      gwcb_.invprod_parse = Q.input();
      valid_.input(lc);
      valid_from_.input(lc);
      valid_until_.input(lc);
      dev_key_info_.input(lc);
      dev_key_.input(lc);
      dev_key_pkx_.input(lc);
      dev_key_pky_.input(lc);
      value_digests_.input(lc);
      org_.input(lc);
      for (size_t ai = 0; ai < num_attr_; ++ai) {
        for (size_t i = 0; i < 64 * 2; ++i) {
          attrb_[ai].push_back(lc.template vinput<8>());
        }
        for (size_t j = 0; j < 2; j++) {
          attr_sha_[ai][j].input(Q);
        }
        attr_mso_[ai].input(lc);
        attr_ei_[ai].input(lc);
        attr_ev_[ai].input(lc);
      }
    }
  };
  struct OpenedAttribute {
    v8 attr[96];
  };
  struct PathEntry {
    CborIndex ind;
    size_t l;
    const uint8_t* name;
  };
  explicit mdoc_1f(const LogicCircuit& lc, const EC& ec, const Nat& order)
      : lc_(lc), ec_(ec), order_(order), sha_(lc), r_(lc), cbor_(lc) {}
  void assert_credential(EltW pkX, EltW pkY, EltW hash_tr,
                         OpenedAttribute oa[],
                         const v8 now[], const Witness& vw) const {}
 private:
  EltW repack32(const sha_packed_v32 H[]) const {}
  void assert_hash(const EltW& e, const Witness& vw) const {}
  void assert_attribute(size_t max, const vind& len, const v8 got[],
                        const v8 want[]) const {}
  void assert_path(size_t len, PathEntry p[], const Witness& vw,
                   std::vector<typename Cbor::decode>& dsC,
                   std::vector<typename Cbor::parse_output>& psC) const {}
  Flatsha sha_;
  Routing r_;
  Cbor cbor_;
};