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