github.com/gobitfly/go-ethereum@v1.8.12/swarm/storage/hasherstore.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package storage 18 19 import ( 20 "fmt" 21 "sync" 22 23 "github.com/ethereum/go-ethereum/crypto/sha3" 24 "github.com/ethereum/go-ethereum/swarm/storage/encryption" 25 ) 26 27 type chunkEncryption struct { 28 spanEncryption encryption.Encryption 29 dataEncryption encryption.Encryption 30 } 31 32 type hasherStore struct { 33 store ChunkStore 34 hashFunc SwarmHasher 35 chunkEncryption *chunkEncryption 36 hashSize int // content hash size 37 refSize int64 // reference size (content hash + possibly encryption key) 38 wg *sync.WaitGroup 39 closed chan struct{} 40 } 41 42 func newChunkEncryption(chunkSize, refSize int64) *chunkEncryption { 43 return &chunkEncryption{ 44 spanEncryption: encryption.New(0, uint32(chunkSize/refSize), sha3.NewKeccak256), 45 dataEncryption: encryption.New(int(chunkSize), 0, sha3.NewKeccak256), 46 } 47 } 48 49 // NewHasherStore creates a hasherStore object, which implements Putter and Getter interfaces. 50 // With the HasherStore you can put and get chunk data (which is just []byte) into a ChunkStore 51 // and the hasherStore will take core of encryption/decryption of data if necessary 52 func NewHasherStore(chunkStore ChunkStore, hashFunc SwarmHasher, toEncrypt bool) *hasherStore { 53 var chunkEncryption *chunkEncryption 54 55 hashSize := hashFunc().Size() 56 refSize := int64(hashSize) 57 if toEncrypt { 58 refSize += encryption.KeyLength 59 chunkEncryption = newChunkEncryption(DefaultChunkSize, refSize) 60 } 61 62 return &hasherStore{ 63 store: chunkStore, 64 hashFunc: hashFunc, 65 chunkEncryption: chunkEncryption, 66 hashSize: hashSize, 67 refSize: refSize, 68 wg: &sync.WaitGroup{}, 69 closed: make(chan struct{}), 70 } 71 } 72 73 // Put stores the chunkData into the ChunkStore of the hasherStore and returns the reference. 74 // If hasherStore has a chunkEncryption object, the data will be encrypted. 75 // Asynchronous function, the data will not necessarily be stored when it returns. 76 func (h *hasherStore) Put(chunkData ChunkData) (Reference, error) { 77 c := chunkData 78 size := chunkData.Size() 79 var encryptionKey encryption.Key 80 if h.chunkEncryption != nil { 81 var err error 82 c, encryptionKey, err = h.encryptChunkData(chunkData) 83 if err != nil { 84 return nil, err 85 } 86 } 87 chunk := h.createChunk(c, size) 88 89 h.storeChunk(chunk) 90 91 return Reference(append(chunk.Addr, encryptionKey...)), nil 92 } 93 94 // Get returns data of the chunk with the given reference (retrieved from the ChunkStore of hasherStore). 95 // If the data is encrypted and the reference contains an encryption key, it will be decrypted before 96 // return. 97 func (h *hasherStore) Get(ref Reference) (ChunkData, error) { 98 key, encryptionKey, err := parseReference(ref, h.hashSize) 99 if err != nil { 100 return nil, err 101 } 102 toDecrypt := (encryptionKey != nil) 103 104 chunk, err := h.store.Get(key) 105 if err != nil { 106 return nil, err 107 } 108 109 chunkData := chunk.SData 110 if toDecrypt { 111 var err error 112 chunkData, err = h.decryptChunkData(chunkData, encryptionKey) 113 if err != nil { 114 return nil, err 115 } 116 } 117 return chunkData, nil 118 } 119 120 // Close indicates that no more chunks will be put with the hasherStore, so the Wait 121 // function can return when all the previously put chunks has been stored. 122 func (h *hasherStore) Close() { 123 close(h.closed) 124 } 125 126 // Wait returns when 127 // 1) the Close() function has been called and 128 // 2) all the chunks which has been Put has been stored 129 func (h *hasherStore) Wait() { 130 <-h.closed 131 h.wg.Wait() 132 } 133 134 func (h *hasherStore) createHash(chunkData ChunkData) Address { 135 hasher := h.hashFunc() 136 hasher.ResetWithLength(chunkData[:8]) // 8 bytes of length 137 hasher.Write(chunkData[8:]) // minus 8 []byte length 138 return hasher.Sum(nil) 139 } 140 141 func (h *hasherStore) createChunk(chunkData ChunkData, chunkSize int64) *Chunk { 142 hash := h.createHash(chunkData) 143 chunk := NewChunk(hash, nil) 144 chunk.SData = chunkData 145 chunk.Size = chunkSize 146 147 return chunk 148 } 149 150 func (h *hasherStore) encryptChunkData(chunkData ChunkData) (ChunkData, encryption.Key, error) { 151 if len(chunkData) < 8 { 152 return nil, nil, fmt.Errorf("Invalid ChunkData, min length 8 got %v", len(chunkData)) 153 } 154 155 encryptionKey, err := encryption.GenerateRandomKey() 156 if err != nil { 157 return nil, nil, err 158 } 159 160 encryptedSpan, err := h.chunkEncryption.spanEncryption.Encrypt(chunkData[:8], encryptionKey) 161 if err != nil { 162 return nil, nil, err 163 } 164 encryptedData, err := h.chunkEncryption.dataEncryption.Encrypt(chunkData[8:], encryptionKey) 165 if err != nil { 166 return nil, nil, err 167 } 168 c := make(ChunkData, len(encryptedSpan)+len(encryptedData)) 169 copy(c[:8], encryptedSpan) 170 copy(c[8:], encryptedData) 171 return c, encryptionKey, nil 172 } 173 174 func (h *hasherStore) decryptChunkData(chunkData ChunkData, encryptionKey encryption.Key) (ChunkData, error) { 175 if len(chunkData) < 8 { 176 return nil, fmt.Errorf("Invalid ChunkData, min length 8 got %v", len(chunkData)) 177 } 178 179 decryptedSpan, err := h.chunkEncryption.spanEncryption.Decrypt(chunkData[:8], encryptionKey) 180 if err != nil { 181 return nil, err 182 } 183 184 decryptedData, err := h.chunkEncryption.dataEncryption.Decrypt(chunkData[8:], encryptionKey) 185 if err != nil { 186 return nil, err 187 } 188 189 // removing extra bytes which were just added for padding 190 length := ChunkData(decryptedSpan).Size() 191 for length > DefaultChunkSize { 192 length = length + (DefaultChunkSize - 1) 193 length = length / DefaultChunkSize 194 length *= h.refSize 195 } 196 197 c := make(ChunkData, length+8) 198 copy(c[:8], decryptedSpan) 199 copy(c[8:], decryptedData[:length]) 200 201 return c[:length+8], nil 202 } 203 204 func (h *hasherStore) RefSize() int64 { 205 return h.refSize 206 } 207 208 func (h *hasherStore) storeChunk(chunk *Chunk) { 209 h.wg.Add(1) 210 go func() { 211 <-chunk.dbStoredC 212 h.wg.Done() 213 }() 214 h.store.Put(chunk) 215 } 216 217 func parseReference(ref Reference, hashSize int) (Address, encryption.Key, error) { 218 encryptedKeyLength := hashSize + encryption.KeyLength 219 switch len(ref) { 220 case KeyLength: 221 return Address(ref), nil, nil 222 case encryptedKeyLength: 223 encKeyIdx := len(ref) - encryption.KeyLength 224 return Address(ref[:encKeyIdx]), encryption.Key(ref[encKeyIdx:]), nil 225 default: 226 return nil, nil, fmt.Errorf("Invalid reference length, expected %v or %v got %v", hashSize, encryptedKeyLength, len(ref)) 227 } 228 229 }