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  }