github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfsblock/id.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package kbfsblock 6 7 import ( 8 "encoding" 9 "encoding/binary" 10 "errors" 11 "math" 12 "math/rand" 13 14 "github.com/keybase/client/go/kbfs/kbfscrypto" 15 "github.com/keybase/client/go/kbfs/kbfshash" 16 ) 17 18 // ID is the (usually content-based) ID for a data block. 19 type ID struct { 20 h kbfshash.Hash 21 } 22 23 // ZeroID is a zero-valued ID. 24 var ZeroID = ID{} 25 26 var _ encoding.BinaryMarshaler = ID{} 27 var _ encoding.BinaryUnmarshaler = (*ID)(nil) 28 29 var _ encoding.TextMarshaler = ID{} 30 var _ encoding.TextUnmarshaler = (*ID)(nil) 31 32 const ( 33 // MaxIDStringLength is the maximum length of the string 34 // representation of a ID. 35 MaxIDStringLength = kbfshash.MaxHashStringLength 36 ) 37 38 // IDFromString creates a ID from the given string. If the 39 // returned error is nil, the returned ID is valid. 40 func IDFromString(idStr string) (ID, error) { 41 h, err := kbfshash.HashFromString(idStr) 42 if err != nil { 43 return ID{}, err 44 } 45 return ID{h}, nil 46 } 47 48 // IDFromBytes creates a ID from the given bytes. If the returned error is nil, 49 // the returned ID is valid. 50 func IDFromBytes(idBytes []byte) (ID, error) { 51 h, err := kbfshash.HashFromBytes(idBytes) 52 if err != nil { 53 return ID{}, err 54 } 55 return ID{h}, nil 56 } 57 58 // IsValid returns whether the block ID is valid. A zero block ID is 59 // considered invalid. 60 func (id ID) IsValid() bool { 61 return id.h.IsValid() 62 } 63 64 // Bytes returns the bytes of the block ID. 65 func (id ID) Bytes() []byte { 66 return id.h.Bytes() 67 } 68 69 func (id ID) String() string { 70 return id.h.String() 71 } 72 73 // MarshalBinary implements the encoding.BinaryMarshaler interface for 74 // ID. Returns an error if the ID is invalid and not the zero 75 // ID. 76 func (id ID) MarshalBinary() (data []byte, err error) { 77 return id.h.MarshalBinary() 78 } 79 80 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 81 // for ID. Returns an error if the given byte array is non-empty and 82 // the ID is invalid. 83 func (id *ID) UnmarshalBinary(data []byte) error { 84 return id.h.UnmarshalBinary(data) 85 } 86 87 // MarshalText implements the encoding.TextMarshaler interface for ID. 88 func (id ID) MarshalText() ([]byte, error) { 89 return id.h.MarshalText() 90 } 91 92 // UnmarshalText implements the encoding.TextUnmarshaler interface for 93 // ID. 94 func (id *ID) UnmarshalText(buf []byte) error { 95 return id.h.UnmarshalText(buf) 96 } 97 98 // HashType returns the type used for this ID. 99 func (id *ID) HashType() kbfshash.HashType { 100 return id.h.GetHashType() 101 } 102 103 func makeRandomID(ht kbfshash.HashType) (ID, error) { 104 var dh kbfshash.RawDefaultHash 105 err := kbfscrypto.RandRead(dh[:]) 106 if err != nil { 107 return ID{}, err 108 } 109 h, err := kbfshash.HashFromRaw(ht, dh[:]) 110 if err != nil { 111 return ID{}, err 112 } 113 return ID{h}, nil 114 } 115 116 // MakeTemporaryID generates a temporary block ID with an invalid hash 117 // type using a CSPRNG. This is used for indirect blocks before 118 // they're committed to the server. 119 func MakeTemporaryID() (ID, error) { 120 return makeRandomID(kbfshash.TemporaryHashType) 121 } 122 123 // MakeFakeID generates a fake block ID with a valid hash type using a 124 // CSPRNG. This is used for fake block IDs in tests. 125 func MakeFakeID() (ID, error) { 126 return makeRandomID(kbfshash.DefaultHashType) 127 } 128 129 const ( 130 // UseMathRandForTest tells MakeRandomIDInRange to use math/rand for PRNG. 131 UseMathRandForTest = true 132 // UseRealRandomness tells MakeRandomIDInRange to use crypto/rand for PRNG. 133 UseRealRandomness = false 134 ) 135 136 // MakeRandomIDInRange generates a random block ID using a CSPRNG, distributing 137 // the random variable over the interval [start, end), where the full range is 138 // [0, MaxUint64). This corresponds to a normalized representation of the 139 // range [kbfshash.RawDefaultHash{}, kbfshash.MaxDefaultHash). 140 func MakeRandomIDInRange(start, end float64, mathRandForTest bool) (ID, error) { 141 if start < 0.0 || 1.0 < end || end <= start { 142 return ID{}, errors.New("Expected range within the interval [0.0, 1.0)") 143 } 144 rangeSize := end - start 145 randBuf := [8]byte{} 146 var err error 147 if mathRandForTest { 148 _, err = rand.Read(randBuf[:]) 149 } else { 150 err = kbfscrypto.RandRead(randBuf[:]) 151 } 152 if err != nil { 153 return ID{}, err 154 } 155 // Generate a random unsigned int. Endianness doesn't matter here because 156 // the bytes are random. 157 randUint := binary.BigEndian.Uint64(randBuf[:]) 158 const maxUintFloat = float64(math.MaxUint64) 159 randFloat := float64(randUint) / maxUintFloat 160 // This forms the start. We fill in the rest with zeroes. 161 randFloatInInterval := rangeSize*randFloat + start 162 scaledRandomUint := uint64(randFloatInInterval * maxUintFloat) 163 // Now endianness matters, because we are relying on how the system 164 // represented integers while doing the calculation. 165 var dh kbfshash.RawDefaultHash 166 binary.BigEndian.PutUint64(dh[:], scaledRandomUint) 167 h, err := kbfshash.HashFromRaw(kbfshash.DefaultHashType, dh[:]) 168 if err != nil { 169 return ID{}, err 170 } 171 return ID{h}, nil 172 } 173 174 // MakePermanentID computes the permanent ID of a block given its 175 // encoded and encrypted contents. 176 func MakePermanentID( 177 encodedEncryptedData []byte, encryptionVer kbfscrypto.EncryptionVer) ( 178 ID, error) { 179 h, err := kbfshash.DoHash( 180 encodedEncryptedData, encryptionVer.ToHashType()) 181 if err != nil { 182 return ID{}, err 183 } 184 return ID{h}, nil 185 } 186 187 // VerifyID verifies that the given block ID is the permanent block ID 188 // for the given encoded and encrypted data. 189 func VerifyID(encodedEncryptedData []byte, id ID) error { 190 return id.h.Verify(encodedEncryptedData) 191 } 192 193 // FakeID returns an ID derived from the given byte, suitable for 194 // testing. 195 func FakeID(b byte) ID { 196 dh := kbfshash.RawDefaultHash{b} 197 h, err := kbfshash.HashFromRaw(kbfshash.DefaultHashType, dh[:]) 198 if err != nil { 199 panic(err) 200 } 201 return ID{h} 202 } 203 204 // FakeIDAdd returns an ID derived from the given ID and the given 205 // byte, suitable for testing. 206 func FakeIDAdd(id ID, b byte) ID { 207 return FakeID(id.h.Bytes()[1] + b) 208 } 209 210 // FakeIDMul returns an ID derived from the given ID and given byte 211 // using *, suitable for testing. 212 // 213 // TODO: Fix the test that breaks when this is replaced with 214 // FakeIDAdd. 215 func FakeIDMul(id ID, b byte) ID { 216 return FakeID(id.h.Bytes()[1] * b) 217 }