gitlab.com/SiaPrime/SiaPrime@v1.4.1/crypto/threefish.go (about)

     1  package crypto
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  
     8  	"github.com/dchest/threefish"
     9  	"gitlab.com/NebulousLabs/fastrand"
    10  )
    11  
    12  const (
    13  	// threefishOverhead is the number of bytes added by EncryptBytes.
    14  	threefishOverhead = 0
    15  )
    16  
    17  type (
    18  	// threefishKey is a key used for encrypting and decrypting data.
    19  	threefishKey [threefish.KeySize]byte
    20  )
    21  
    22  // generateThreefishKey produces a threefishKey that can be used for encrypting
    23  // and decrypting data using Threefish.
    24  func generateThreefishKey() (key threefishKey) {
    25  	fastrand.Read(key[:])
    26  	return
    27  }
    28  
    29  // newCipher creates a new Threefish cipher from the key.
    30  func (key threefishKey) newCipher() *threefish.Threefish {
    31  	cipher, err := threefish.NewCipher(key[:], make([]byte, threefish.TweakSize))
    32  	if err != nil {
    33  		panic("NewCipher only returns an error if len(key) != 64")
    34  	}
    35  	return cipher
    36  }
    37  
    38  // newThreefishKey creates a new threefishKey from a given entropy.
    39  func newThreefishKey(entropy []byte) (key threefishKey, err error) {
    40  	// check key length
    41  	if len(entropy) != threefish.KeySize {
    42  		err = fmt.Errorf("threefish key should have size %v but was %v",
    43  			threefish.KeySize, len(entropy))
    44  		return
    45  	}
    46  	// create key
    47  	copy(key[:], entropy)
    48  	return
    49  }
    50  
    51  // DecryptBytes decrypts a ciphertext created by EncryptPiece. The tweak is
    52  // expected to be incremented by 1 for every 64 bytes of data.
    53  func (key threefishKey) DecryptBytes(ct Ciphertext) ([]byte, error) {
    54  	// Check if input has correct length.
    55  	if len(ct)%threefish.BlockSize != 0 {
    56  		return nil, fmt.Errorf("supplied ciphertext is not a multiple of %v", threefish.BlockSize)
    57  	}
    58  
    59  	// Create the cipher
    60  	cipher := key.newCipher()
    61  
    62  	// Create the initial tweak and plaintext slice.
    63  	tweak := make([]byte, threefish.TweakSize)
    64  	plaintext := make([]byte, len(ct))
    65  
    66  	// Decrypt the ciphertext one block at a time while incrementing the tweak.
    67  	cbuf := bytes.NewBuffer(ct)
    68  	pbuf := bytes.NewBuffer(plaintext)
    69  	for cbuf.Len() > 0 {
    70  		// Decrypt the block.
    71  		cipher.Decrypt(pbuf.Next(threefish.BlockSize), cbuf.Next(threefish.BlockSize))
    72  
    73  		// Increment the tweak by 1.
    74  		tweakNum := binary.LittleEndian.Uint64(tweak)
    75  		binary.LittleEndian.PutUint64(tweak, tweakNum+1)
    76  		if err := cipher.SetTweak(tweak); err != nil {
    77  			panic(err)
    78  		}
    79  	}
    80  	return plaintext, nil
    81  }
    82  
    83  // DecryptBytesInPlace decrypts a ciphertext created by EncryptPiece. The
    84  // blockIndex is expected to be incremented by 1 for every 64 bytes of data as
    85  // it represents the offset within a larger piece of data, allowing for partial
    86  // decryption. e.g. If the provided ciphertext starts at offset 64 of the
    87  // original ciphertext produced by EncryptPiece, it can be decrypted by setting
    88  // blockIndex to 1.
    89  // DecryptBytesInPlace reuses the memory of ct to be able to operate in-place.
    90  // This means that ct can't be reused after calling DecryptBytesInPlace.
    91  func (key threefishKey) DecryptBytesInPlace(ct Ciphertext, blockIndex uint64) ([]byte, error) {
    92  	// Check if input has correct length.
    93  	if len(ct)%threefish.BlockSize != 0 {
    94  		return nil, fmt.Errorf("supplied ciphertext is not a multiple of %v", threefish.BlockSize)
    95  	}
    96  
    97  	// Create the cipher
    98  	cipher := key.newCipher()
    99  
   100  	// Create the initial tweak.
   101  	tweak := make([]byte, threefish.TweakSize)
   102  	binary.LittleEndian.PutUint64(tweak, blockIndex)
   103  	if err := cipher.SetTweak(tweak); err != nil {
   104  		panic(err)
   105  	}
   106  
   107  	// Decrypt the ciphertext one block at a time while incrementing the tweak.
   108  	buf := bytes.NewBuffer(ct)
   109  	dst := ct
   110  	for block := buf.Next(threefish.BlockSize); len(block) > 0; block = buf.Next(threefish.BlockSize) {
   111  		// Decrypt the block.
   112  		cipher.Decrypt(dst, block)
   113  
   114  		// Increment the tweak by 1.
   115  		tweakNum := binary.LittleEndian.Uint64(tweak)
   116  		binary.LittleEndian.PutUint64(tweak, tweakNum+1)
   117  		if err := cipher.SetTweak(tweak); err != nil {
   118  			panic(err)
   119  		}
   120  
   121  		// Adjust the dst.
   122  		dst = dst[threefish.BlockSize:]
   123  	}
   124  	return ct[:], nil
   125  }
   126  
   127  // Derive derives a child key for a given combination of chunk and piece index.
   128  func (key threefishKey) Derive(chunkIndex, pieceIndex uint64) CipherKey {
   129  	entropy1 := HashAll(key[:], chunkIndex, pieceIndex, 0)
   130  	entropy2 := HashAll(key[:], chunkIndex, pieceIndex, 1)
   131  	ck, err := NewSiaKey(TypeThreefish, append(entropy1[:], entropy2[:]...))
   132  	if err != nil {
   133  		panic("this should not be possible when deriving from a valid key")
   134  	}
   135  	return ck
   136  }
   137  
   138  // EncryptBytes encrypts arbitrary data using the ThreefishKey and using a
   139  // different tweak for every 64 byte block.
   140  func (key threefishKey) EncryptBytes(piece []byte) Ciphertext {
   141  	// Sanity check piece length.
   142  	if len(piece)%threefish.BlockSize != 0 {
   143  		panic("piece must be multiple of threefish.BlockSize")
   144  	}
   145  
   146  	// Create the cipher.
   147  	cipher := key.newCipher()
   148  
   149  	// Create the initial tweak and ciphertext slice.
   150  	tweak := make([]byte, threefish.TweakSize)
   151  	ciphertext := make([]byte, len(piece))
   152  
   153  	// Encrypt the piece one block at a time while incrementing the tweak.
   154  	buf := bytes.NewBuffer(piece)
   155  	dst := ciphertext
   156  	for block := buf.Next(threefish.BlockSize); len(block) > 0; block = buf.Next(threefish.BlockSize) {
   157  		// Encrypt the block.
   158  		cipher.Encrypt(dst, block)
   159  
   160  		// Increment the tweak by 1.
   161  		tweakNum := binary.LittleEndian.Uint64(tweak)
   162  		binary.LittleEndian.PutUint64(tweak, tweakNum+1)
   163  		if err := cipher.SetTweak(tweak); err != nil {
   164  			panic(err)
   165  		}
   166  
   167  		// Adjust the dst.
   168  		dst = dst[threefish.BlockSize:]
   169  	}
   170  	return ciphertext
   171  }
   172  
   173  // Key returns the threefish key.
   174  func (key threefishKey) Key() []byte {
   175  	return key[:]
   176  }
   177  
   178  // Type returns the type of the threefish key.
   179  func (threefishKey) Type() CipherType {
   180  	return TypeThreefish
   181  }