// Package xts implements the XTS cipher mode as specified in IEEE P1619/D16. // // XTS mode is typically used for disk encryption, which presents a number of // novel problems that make more common modes inapplicable. The disk is // conceptually an array of sectors and we must be able to encrypt and decrypt // a sector in isolation. However, an attacker must not be able to transpose // two sectors of plaintext by transposing their ciphertext. // // XTS wraps a block cipher with Rogaway's XEX mode in order to build a // tweakable block cipher. This allows each sector to have a unique tweak and // effectively create a unique key for each sector. // // XTS does not provide any authentication. An attacker can manipulate the // ciphertext and randomise a block (16 bytes) of the plaintext. This package // does not implement ciphertext-stealing so sectors must be a multiple of 16 // bytes. // // Note that XTS is usually not appropriate for any use besides disk encryption. // Most users should use an AEAD mode like GCM (from crypto/cipher.NewGCM) instead. package blockmode import ( "xdx.jelly/xgcl/gerrors" "xdx.jelly/xgcl/internal/xor" ) // xts contains an expanded key structure. It is safe for concurrent use if // the underlying block cipher is safe for concurrent use. type xts struct { k1, k2 EcbBlockMode tweak []byte outBuf []byte } var _ TernaryCrypter = &xts{} // xtsBlockSize is the block size that the underlying cipher must have. XTS is // only defined for 16-byte ciphers. const xtsBlockSize = 16 // NewXTS 需要使用两个16字节key, 对应入参需要两个EcbEncBlockMode。 func NewXTS(ecb1, ecb2 EcbBlockMode) *xts { return &xts{ k1: ecb1, k2: ecb2, tweak: make([]byte, 0, xtsBlockSize), outBuf: make([]byte, 0, 2*xtsBlockSize), } } func (x *xts) EncryptInit(tw []byte) error { if len(tw) != xtsBlockSize { return gerrors.WithAnnotating(ErrXTSEncFailed, "input tweak must be 16 bytes long") } if cap(x.outBuf) < 2*xtsBlockSize { x.outBuf = make([]byte, 0, 2*xtsBlockSize) } x.outBuf = x.outBuf[:0] x.tweak = append(x.tweak[:0], tw...) _ = x.k2.EcbEncCryptBlocks(x.tweak, x.tweak) return nil } func (x *xts) Encrypt(dst []byte, src []byte) ([]byte, error) { dst, err := x.EncryptUpdate(dst, src) if err != nil { return nil, err } return x.EncryptFinal(dst) } func (x *xts) fillTweakMask(mask []byte) { for i := 0; i < len(mask); i += xtsBlockSize { copy(mask[i:i+xtsBlockSize], x.tweak[:]) mul2(x.tweak) } } func (x *xts) EncryptUpdate(dst []byte, in []byte) ([]byte, error) { // how many data in hand dataLen := len(x.outBuf) + len(in) if dataLen < 2*xtsBlockSize { x.outBuf = append(x.outBuf, in...) return dst, nil } // how many blocks to process in this update m := (dataLen - xtsBlockSize) >> 4 mask := make([]byte, m<<4) x.fillTweakMask(mask) ret, out := sliceForAppend(dst, m<<4) n := xor.XorBytes(out, mask, x.outBuf) // TODO wrong if len(in) = 1 n = xor.XorBytes(out[n:], mask[n:], in) in = in[n:] _ = x.k1.EcbEncCryptBlocks(out, out) xor.XorBytes(out, out, mask) n = 0 if len(x.outBuf) > m<<4 { n = copy(x.outBuf[:xtsBlockSize], x.outBuf[m<<4:]) } x.outBuf = x.outBuf[:n] x.outBuf = append(x.outBuf, in...) return ret, nil } func (x *xts) EncryptFinal(dst []byte) ([]byte, error) { if len(x.outBuf) < xtsBlockSize { return dst, gerrors.WithAnnotating(ErrXTSEncFailed, "plaintext must be at least 16 bytes") } in0 := x.outBuf[:xtsBlockSize] in1 := x.outBuf[xtsBlockSize:] ret, out := sliceForAppend(dst, len(x.outBuf)) out0 := out[:xtsBlockSize] out1 := out[xtsBlockSize:] mask := make([]byte, xtsBlockSize) x.fillTweakMask(mask) xor.XorBytes(out0, mask, in0) _ = x.k1.EcbEncCryptBlocks(out0, out0) xor.XorBytes(out0, out0, mask) if len(in1) > 0 { lastBlock := make([]byte, len(in1), xtsBlockSize) copy(lastBlock, in1) lastBlock = append(lastBlock, out0[len(in1):]...) x.fillTweakMask(mask) xor.XorBytes(lastBlock, mask, lastBlock) _ = x.k1.EcbEncCryptBlocks(lastBlock, lastBlock) xor.XorBytes(lastBlock, lastBlock, mask) copy(mask, out0) copy(out0, lastBlock) copy(out1, mask) } return ret, nil } func (x *xts) DecryptInit(tw []byte) error { if len(tw) != xtsBlockSize { return gerrors.WithAnnotating(ErrXTSDecFailed, "input tweak must be 16 bytes long") } x.tweak = append(x.tweak[:0], tw...) if cap(x.outBuf) < 2*xtsBlockSize { x.outBuf = make([]byte, 0, 2*xtsBlockSize) } x.outBuf = x.outBuf[:0] _ = x.k2.EcbEncCryptBlocks(x.tweak, x.tweak) return nil } func (x *xts) Decrypt(dst []byte, in []byte) ([]byte, error) { dst, err := x.DecryptUpdate(dst, in) if err != nil { return nil, err } return x.DecryptFinal(dst) } func (x *xts) DecryptUpdate(dst []byte, in []byte) ([]byte, error) { // how many data in hand dataLen := len(x.outBuf) + len(in) if dataLen < 2*xtsBlockSize { x.outBuf = append(x.outBuf, in...) return dst, nil } // how many blocks to process in this update m := (dataLen - xtsBlockSize) >> 4 mask := make([]byte, m<<4) x.fillTweakMask(mask) ret, out := sliceForAppend(dst, m<<4) n := xor.XorBytes(out, mask, x.outBuf) // TODO wrong if len(in) = 1 n = xor.XorBytes(out[n:], mask[n:], in) in = in[n:] _ = x.k1.EcbDecCryptBlocks(out, out) xor.XorBytes(out, out, mask) n = 0 if len(x.outBuf) > m<<4 { n = copy(x.outBuf[:xtsBlockSize], x.outBuf[m<<4:]) } x.outBuf = x.outBuf[:n] x.outBuf = append(x.outBuf, in...) return ret, nil } func (x *xts) DecryptFinal(dst []byte) ([]byte, error) { if len(x.outBuf) < xtsBlockSize { return dst, gerrors.WithAnnotating(ErrXTSEncFailed, "plaintext must be at least 16 bytes") } in0 := x.outBuf[:xtsBlockSize] in1 := x.outBuf[xtsBlockSize:] ret, out := sliceForAppend(dst, len(x.outBuf)) out0 := out[:xtsBlockSize] out1 := out[xtsBlockSize:] mask := make([]byte, xtsBlockSize*2) x.fillTweakMask(mask) mask0 := mask[:xtsBlockSize] mask1 := mask[xtsBlockSize:] if len(in1) == 0 { xor.XorBytes(out0, mask0, in0) _ = x.k1.EcbDecCryptBlocks(out0, out0) xor.XorBytes(out0, out0, mask0) } else { xor.XorBytes(out0, mask1, in0) _ = x.k1.EcbDecCryptBlocks(out0, out0) xor.XorBytes(out0, out0, mask1) lastBlock := make([]byte, len(in1), xtsBlockSize) copy(lastBlock, in1) lastBlock = append(lastBlock, out0[len(in1):]...) xor.XorBytes(lastBlock, mask0, lastBlock) _ = x.k1.EcbDecCryptBlocks(lastBlock, lastBlock) xor.XorBytes(lastBlock, lastBlock, mask0) copy(mask, out0) copy(out0, lastBlock) copy(out1, mask) } return ret, nil } // mul2 multiplies tweak by 2 in GF(2¹²⁸) with an irreducible polynomial of // x¹²⁸ + x⁷ + x² + x + 1. func mul2(tweak []byte) { var carryIn byte for j := range tweak { carryOut := tweak[j] >> 7 tweak[j] = (tweak[j] << 1) + carryIn carryIn = carryOut } if carryIn != 0 { // If we have a carry bit then we need to subtract a multiple // of the irreducible polynomial (x¹²⁸ + x⁷ + x² + x + 1). // By dropping the carry bit, we're subtracting the x^128 term // so all that remains is to subtract x⁷ + x² + x + 1. // Subtraction (and addition) in this representation is just // XOR. tweak[0] ^= 1<<7 | 1<<2 | 1<<1 | 1 } }