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 }