github.com/shyftnetwork/go-empyrean@v1.8.3-0.20191127201940-fbfca9338f04/swarm/storage/encryption/encryption.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 encryption
    18  
    19  import (
    20  	"crypto/rand"
    21  	"encoding/binary"
    22  	"fmt"
    23  	"hash"
    24  	"sync"
    25  )
    26  
    27  const KeyLength = 32
    28  
    29  type Key []byte
    30  
    31  type Encryption interface {
    32  	Encrypt(data []byte) ([]byte, error)
    33  	Decrypt(data []byte) ([]byte, error)
    34  }
    35  
    36  type encryption struct {
    37  	key      Key              // the encryption key (hashSize bytes long)
    38  	keyLen   int              // length of the key = length of blockcipher block
    39  	padding  int              // encryption will pad the data upto this if > 0
    40  	initCtr  uint32           // initial counter used for counter mode blockcipher
    41  	hashFunc func() hash.Hash // hasher constructor function
    42  }
    43  
    44  // New constructs a new encryptor/decryptor
    45  func New(key Key, padding int, initCtr uint32, hashFunc func() hash.Hash) *encryption {
    46  	return &encryption{
    47  		key:      key,
    48  		keyLen:   len(key),
    49  		padding:  padding,
    50  		initCtr:  initCtr,
    51  		hashFunc: hashFunc,
    52  	}
    53  }
    54  
    55  // Encrypt encrypts the data and does padding if specified
    56  func (e *encryption) Encrypt(data []byte) ([]byte, error) {
    57  	length := len(data)
    58  	outLength := length
    59  	isFixedPadding := e.padding > 0
    60  	if isFixedPadding {
    61  		if length > e.padding {
    62  			return nil, fmt.Errorf("Data length longer than padding, data length %v padding %v", length, e.padding)
    63  		}
    64  		outLength = e.padding
    65  	}
    66  	out := make([]byte, outLength)
    67  	e.transform(data, out)
    68  	return out, nil
    69  }
    70  
    71  // Decrypt decrypts the data, if padding was used caller must know original length and truncate
    72  func (e *encryption) Decrypt(data []byte) ([]byte, error) {
    73  	length := len(data)
    74  	if e.padding > 0 && length != e.padding {
    75  		return nil, fmt.Errorf("Data length different than padding, data length %v padding %v", length, e.padding)
    76  	}
    77  	out := make([]byte, length)
    78  	e.transform(data, out)
    79  	return out, nil
    80  }
    81  
    82  //
    83  func (e *encryption) transform(in, out []byte) {
    84  	inLength := len(in)
    85  	wg := sync.WaitGroup{}
    86  	wg.Add((inLength-1)/e.keyLen + 1)
    87  	for i := 0; i < inLength; i += e.keyLen {
    88  		l := min(e.keyLen, inLength-i)
    89  		// call transformations per segment (asyncronously)
    90  		go func(i int, x, y []byte) {
    91  			defer wg.Done()
    92  			e.Transcrypt(i, x, y)
    93  		}(i/e.keyLen, in[i:i+l], out[i:i+l])
    94  	}
    95  	// pad the rest if out is longer
    96  	pad(out[inLength:])
    97  	wg.Wait()
    98  }
    99  
   100  // used for segmentwise transformation
   101  // if in is shorter than out, padding is used
   102  func (e *encryption) Transcrypt(i int, in []byte, out []byte) {
   103  	// first hash key with counter (initial counter + i)
   104  	hasher := e.hashFunc()
   105  	hasher.Write(e.key)
   106  
   107  	ctrBytes := make([]byte, 4)
   108  	binary.LittleEndian.PutUint32(ctrBytes, uint32(i)+e.initCtr)
   109  	hasher.Write(ctrBytes)
   110  
   111  	ctrHash := hasher.Sum(nil)
   112  	hasher.Reset()
   113  
   114  	// second round of hashing for selective disclosure
   115  	hasher.Write(ctrHash)
   116  	segmentKey := hasher.Sum(nil)
   117  	hasher.Reset()
   118  
   119  	// XOR bytes uptil length of in (out must be at least as long)
   120  	inLength := len(in)
   121  	for j := 0; j < inLength; j++ {
   122  		out[j] = in[j] ^ segmentKey[j]
   123  	}
   124  	// insert padding if out is longer
   125  	pad(out[inLength:])
   126  }
   127  
   128  func pad(b []byte) {
   129  	l := len(b)
   130  	for total := 0; total < l; {
   131  		read, _ := rand.Read(b[total:])
   132  		total += read
   133  	}
   134  }
   135  
   136  // GenerateRandomKey generates a random key of length l
   137  func GenerateRandomKey(l int) Key {
   138  	key := make([]byte, l)
   139  	var total int
   140  	for total < l {
   141  		read, _ := rand.Read(key[total:])
   142  		total += read
   143  	}
   144  	return key
   145  }
   146  
   147  func min(x, y int) int {
   148  	if x < y {
   149  		return x
   150  	}
   151  	return y
   152  }