aboutsummaryrefslogtreecommitdiffstats
path: root/swarm/storage/encryption/encryption.go
diff options
context:
space:
mode:
Diffstat (limited to 'swarm/storage/encryption/encryption.go')
-rw-r--r--swarm/storage/encryption/encryption.go126
1 files changed, 81 insertions, 45 deletions
diff --git a/swarm/storage/encryption/encryption.go b/swarm/storage/encryption/encryption.go
index e50f2163d..6fbdab062 100644
--- a/swarm/storage/encryption/encryption.go
+++ b/swarm/storage/encryption/encryption.go
@@ -21,6 +21,7 @@ import (
"encoding/binary"
"fmt"
"hash"
+ "sync"
)
const KeyLength = 32
@@ -28,84 +29,119 @@ const KeyLength = 32
type Key []byte
type Encryption interface {
- Encrypt(data []byte, key Key) ([]byte, error)
- Decrypt(data []byte, key Key) ([]byte, error)
+ Encrypt(data []byte) ([]byte, error)
+ Decrypt(data []byte) ([]byte, error)
}
type encryption struct {
- padding int
- initCtr uint32
- hashFunc func() hash.Hash
+ key Key // the encryption key (hashSize bytes long)
+ keyLen int // length of the key = length of blockcipher block
+ padding int // encryption will pad the data upto this if > 0
+ initCtr uint32 // initial counter used for counter mode blockcipher
+ hashFunc func() hash.Hash // hasher constructor function
}
-func New(padding int, initCtr uint32, hashFunc func() hash.Hash) *encryption {
+// New constructs a new encryptor/decryptor
+func New(key Key, padding int, initCtr uint32, hashFunc func() hash.Hash) *encryption {
return &encryption{
+ key: key,
+ keyLen: len(key),
padding: padding,
initCtr: initCtr,
hashFunc: hashFunc,
}
}
-func (e *encryption) Encrypt(data []byte, key Key) ([]byte, error) {
+// Encrypt encrypts the data and does padding if specified
+func (e *encryption) Encrypt(data []byte) ([]byte, error) {
length := len(data)
+ outLength := length
isFixedPadding := e.padding > 0
- if isFixedPadding && length > e.padding {
- return nil, fmt.Errorf("Data length longer than padding, data length %v padding %v", length, e.padding)
- }
-
- paddedData := data
- if isFixedPadding && length < e.padding {
- paddedData = make([]byte, e.padding)
- copy(paddedData[:length], data)
- rand.Read(paddedData[length:])
+ if isFixedPadding {
+ if length > e.padding {
+ return nil, fmt.Errorf("Data length longer than padding, data length %v padding %v", length, e.padding)
+ }
+ outLength = e.padding
}
- return e.transform(paddedData, key), nil
+ out := make([]byte, outLength)
+ e.transform(data, out)
+ return out, nil
}
-func (e *encryption) Decrypt(data []byte, key Key) ([]byte, error) {
+// Decrypt decrypts the data, if padding was used caller must know original length and truncate
+func (e *encryption) Decrypt(data []byte) ([]byte, error) {
length := len(data)
if e.padding > 0 && length != e.padding {
return nil, fmt.Errorf("Data length different than padding, data length %v padding %v", length, e.padding)
}
+ out := make([]byte, length)
+ e.transform(data, out)
+ return out, nil
+}
- return e.transform(data, key), nil
+//
+func (e *encryption) transform(in, out []byte) {
+ inLength := len(in)
+ wg := sync.WaitGroup{}
+ wg.Add((inLength-1)/e.keyLen + 1)
+ for i := 0; i < inLength; i += e.keyLen {
+ l := min(e.keyLen, inLength-i)
+ // call transformations per segment (asyncronously)
+ go func(i int, x, y []byte) {
+ defer wg.Done()
+ e.Transcrypt(i, x, y)
+ }(i/e.keyLen, in[i:i+l], out[i:i+l])
+ }
+ // pad the rest if out is longer
+ pad(out[inLength:])
+ wg.Wait()
}
-func (e *encryption) transform(data []byte, key Key) []byte {
- dataLength := len(data)
- transformedData := make([]byte, dataLength)
+// used for segmentwise transformation
+// if in is shorter than out, padding is used
+func (e *encryption) Transcrypt(i int, in []byte, out []byte) {
+ // first hash key with counter (initial counter + i)
hasher := e.hashFunc()
- ctr := e.initCtr
- hashSize := hasher.Size()
- for i := 0; i < dataLength; i += hashSize {
- hasher.Write(key)
+ hasher.Write(e.key)
- ctrBytes := make([]byte, 4)
- binary.LittleEndian.PutUint32(ctrBytes, ctr)
+ ctrBytes := make([]byte, 4)
+ binary.LittleEndian.PutUint32(ctrBytes, uint32(i)+e.initCtr)
+ hasher.Write(ctrBytes)
- hasher.Write(ctrBytes)
+ ctrHash := hasher.Sum(nil)
+ hasher.Reset()
- ctrHash := hasher.Sum(nil)
- hasher.Reset()
- hasher.Write(ctrHash)
+ // second round of hashing for selective disclosure
+ hasher.Write(ctrHash)
+ segmentKey := hasher.Sum(nil)
+ hasher.Reset()
- segmentKey := hasher.Sum(nil)
-
- hasher.Reset()
+ // XOR bytes uptil length of in (out must be at least as long)
+ inLength := len(in)
+ for j := 0; j < inLength; j++ {
+ out[j] = in[j] ^ segmentKey[j]
+ }
+ // insert padding if out is longer
+ pad(out[inLength:])
+}
- segmentSize := min(hashSize, dataLength-i)
- for j := 0; j < segmentSize; j++ {
- transformedData[i+j] = data[i+j] ^ segmentKey[j]
- }
- ctr++
+func pad(b []byte) {
+ l := len(b)
+ for total := 0; total < l; {
+ read, _ := rand.Read(b[total:])
+ total += read
}
- return transformedData
}
-func GenerateRandomKey() (Key, error) {
- key := make([]byte, KeyLength)
- _, err := rand.Read(key)
- return key, err
+// GenerateRandomKey generates a random key of length l
+func GenerateRandomKey(l int) Key {
+ key := make([]byte, l)
+ var total int
+ for total < l {
+ read, _ := rand.Read(key[total:])
+ total += read
+ }
+ return key
}
func min(x, y int) int {