Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
transcript.h
1// Copyright 2025 Google LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef PRIVACY_PROOFS_ZK_LIB_RANDOM_TRANSCRIPT_H_
16#define PRIVACY_PROOFS_ZK_LIB_RANDOM_TRANSCRIPT_H_
17
18#include <cstddef>
19#include <cstdint>
20#include <cstring>
21#include <memory>
22
23#include "random/random.h"
24#include "util/crypto.h"
25#include "util/panic.h"
26#include "util/serialization.h"
27
28namespace proofs {
29
30/*
31FSPRF and Transcript together used implement the Fiat-Shamir transform.
32*/
33class FSPRF {
34 public:
35 explicit FSPRF(const uint8_t key[kPRFKeySize])
36 : prf_(key), nblock_(0), rdptr_(kPRFOutputSize) {}
37
38 // Disable copy for good measure.
39 explicit FSPRF(const FSPRF&) = delete;
40 FSPRF& operator=(const FSPRF&) = delete;
41
42 void bytes(uint8_t buf[/*n*/], size_t n) {
43 while (n-- > 0) {
44 if (rdptr_ == kPRFOutputSize) {
45 refill();
46 }
47 *buf++ = saved_[rdptr_++];
48 }
49 }
50
51 private:
52 void refill() {
53 uint8_t in[kPRFInputSize] = {};
54 u64_to_le(in, nblock_++);
55 prf_.Eval(saved_, in);
56 rdptr_ = 0;
57 }
58
59 PRF prf_;
60 uint64_t nblock_;
61 size_t rdptr_; // read pointer into saved[]
62 uint8_t saved_[kPRFOutputSize]; // saved pseudo-random bytes
63};
64
65class Transcript : public RandomEngine {
66 enum { TAG_BSTR = 0, TAG_FIELD_ELEM = 1, TAG_ARRAY = 1 };
67
68 public:
69 // A transcript must be explicitly initialized so that each instance of
70 // the Random oracle is unique.
71 Transcript(const uint8_t init[], size_t init_len) : sha_(), prf_() {
72 write(init, init_len);
73 }
74
75 // Remove default copy and move implementations.
76 Transcript(const Transcript&) = delete;
77 Transcript& operator=(const Transcript&) = delete;
78
79 // Explicit copy to avoid accidental passing by value.
80 Transcript clone() { return Transcript(sha_); }
81
82 // Generate bytes by via the current FSPRF object.
83 void bytes(uint8_t buf[/*n*/], size_t n) override {
84 if (!prf_) {
85 uint8_t key[kPRFKeySize];
86 get(key);
87 prf_ = std::make_unique<FSPRF>(key);
88 }
89 prf_->bytes(buf, n);
90 }
91
92 // snapshot the hash of the transcript so far
93 void get(uint8_t key[/*kPRFKeySize*/]) {
94 check(kPRFKeySize == kSHA256DigestSize, "prf key size != digest output");
95 // fork the state because we will finalize it
96 SHA256 tmp_hash;
97 tmp_hash.CopyState(sha_);
98 tmp_hash.DigestData(key);
99 }
100
101 // Typed write operations. We tag byte-array(n), field-element, and
102 // array-of-field-element(n).
103 //
104 // We make a few arbitrary choices that make no real difference.
105 // All lengths are 64-bit. We distinguish a field element from
106 // an array of one field element, which is kind of arbitrary.
107
108 // byte string
109 void write(const uint8_t data[/*n*/], size_t n) {
110 tag(TAG_BSTR);
111 length(n);
112
113 write_untyped(data, n);
114 }
115
116 // N zero bytes
117 void write0(size_t n) {
118 tag(TAG_BSTR);
119 length(n);
120
121 uint8_t data[32] = {};
122 for (; n > 32; n -= 32) {
123 write_untyped(data, 32);
124 }
125 write_untyped(data, n);
126 }
127
128 // one field element
129 template <class Field>
130 void write(const typename Field::Elt& e, const Field& F) {
131 tag(TAG_FIELD_ELEM);
132
133 write_untyped(e, F);
134 }
135
136 // array of field elements
137 template <class Field>
138 void write(const typename Field::Elt e[/*n*/], size_t ince, size_t n,
139 const Field& F) {
140 tag(TAG_ARRAY);
141 length(n);
142
143 for (size_t i = 0; i < n; ++i) {
144 write_untyped(e[i * ince], F);
145 }
146 }
147
148 private:
149 explicit Transcript(const SHA256& sha) : sha_() {
150 sha_.CopyState(sha);
151 }
152
153 // Output a 1-byte tag
154 void tag(size_t t) {
155 uint8_t d = static_cast<uint8_t>(t);
156 write_untyped(&d, 1);
157 }
158
159 // Output a 8-byte length. We pass the length
160 // as size_t, but we always write it as uint64_t
161 void length(size_t x) {
162 uint8_t a[8];
163 u64_to_le(a, x);
164 write_untyped(a, 8);
165 }
166
167 void write_untyped(const uint8_t data[/*n*/], size_t n) {
168 // invalidate the PRF on any writes
169 prf_.reset();
170 sha_.Update(data, n);
171 }
172
173 template <class Field>
174 void write_untyped(const typename Field::Elt& e, const Field& F) {
175 uint8_t buf[Field::kBytes];
176 F.to_bytes_field(buf, e);
177 write_untyped(buf, sizeof(buf));
178 }
179
180 SHA256 sha_;
181 std::unique_ptr<FSPRF> prf_;
182};
183} // namespace proofs
184
185#endif // PRIVACY_PROOFS_ZK_LIB_RANDOM_TRANSCRIPT_H_
Definition crypto.h:69
Definition random.h:32
Definition crypto.h:40
Definition gf2_128.h:63