github.com/consensys/gnark-crypto@v0.14.0/fiat-shamir/transcript.go (about) 1 // Copyright 2020 ConsenSys Software Inc. 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 package fiatshamir 16 17 import ( 18 "errors" 19 "fmt" 20 "hash" 21 ) 22 23 // errChallengeNotFound is returned when a wrong challenge name is provided. 24 var ( 25 errChallengeNotFound = errors.New("challenge not recorded in the transcript") 26 errChallengeAlreadyComputed = errors.New("challenge already computed, cannot be binded to other values") 27 errPreviousChallengeNotComputed = errors.New("the previous challenge is needed and has not been computed") 28 ) 29 30 // Transcript handles the creation of challenges for Fiat Shamir. 31 type Transcript struct { 32 // hash function that is used. 33 h hash.Hash 34 35 challenges map[string]challenge 36 previous *challenge 37 } 38 39 type challenge struct { 40 position int // position of the challenge in the Transcript. order matters. 41 bindings [][]byte // bindings stores the variables a challenge is binded to. 42 value []byte // value stores the computed challenge 43 isComputed bool 44 } 45 46 // NewTranscript returns a new transcript. 47 // h is the hash function that is used to compute the challenges. 48 // challenges are the name of the challenges. The order of the challenges IDs matters. 49 func NewTranscript(h hash.Hash, challengesID ...string) *Transcript { 50 challenges := make(map[string]challenge) 51 for i := range challengesID { 52 challenges[challengesID[i]] = challenge{position: i} 53 } 54 t := &Transcript{ 55 challenges: challenges, 56 h: h, 57 } 58 return t 59 } 60 61 // Bind binds the challenge to value. A challenge can be binded to an 62 // arbitrary number of values, but the order in which the binded values 63 // are added is important. Once a challenge is computed, it cannot be 64 // binded to other values. 65 func (t *Transcript) Bind(challengeID string, bValue []byte) error { 66 67 currentChallenge, ok := t.challenges[challengeID] 68 if !ok { 69 return errChallengeNotFound 70 } 71 72 if currentChallenge.isComputed { 73 return errChallengeAlreadyComputed 74 } 75 76 bCopy := make([]byte, len(bValue)) 77 copy(bCopy, bValue) 78 currentChallenge.bindings = append(currentChallenge.bindings, bCopy) 79 t.challenges[challengeID] = currentChallenge 80 81 return nil 82 83 } 84 85 // ComputeChallenge computes the challenge corresponding to the given name. 86 // The challenge is: 87 // * H(name || previous_challenge || binded_values...) if the challenge is not the first one 88 // * H(name || binded_values... ) if it is the first challenge 89 func (t *Transcript) ComputeChallenge(challengeID string) ([]byte, error) { 90 91 challenge, ok := t.challenges[challengeID] 92 if !ok { 93 return nil, errChallengeNotFound 94 } 95 96 // if the challenge was already computed we return it 97 if challenge.isComputed { 98 return challenge.value, nil 99 } 100 101 // reset before populating the internal state 102 t.h.Reset() 103 defer t.h.Reset() 104 105 if _, err := t.h.Write([]byte(challengeID)); err != nil { 106 return nil, fmt.Errorf("write: %w", err) 107 } 108 109 // write the previous challenge if it's not the first challenge 110 if challenge.position != 0 { 111 if t.previous == nil || (t.previous.position != challenge.position-1) { 112 return nil, errPreviousChallengeNotComputed 113 } 114 if _, err := t.h.Write(t.previous.value[:]); err != nil { 115 return nil, err 116 } 117 } 118 119 // write the binded values in the order they were added 120 for _, b := range challenge.bindings { 121 if _, err := t.h.Write(b); err != nil { 122 return nil, err 123 } 124 } 125 126 // compute the hash of the accumulated values 127 res := t.h.Sum(nil) 128 129 challenge.value = make([]byte, len(res)) 130 copy(challenge.value, res) 131 challenge.isComputed = true 132 133 t.challenges[challengeID] = challenge 134 t.previous = &challenge 135 136 return res, nil 137 138 }