init: v1.0.0
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user