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