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  }