mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-28 13:16:19 +08:00
cipher: refactor xts mode #149
This commit is contained in:
parent
71ab69ef9b
commit
9d6e46cafd
275
cipher/xts.go
275
cipher/xts.go
@ -19,102 +19,170 @@ type concurrentBlocks interface {
|
|||||||
DecryptBlocks(dst, src []byte)
|
DecryptBlocks(dst, src []byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A XTSBlockMode represents a block cipher running in a XTS mode
|
|
||||||
type XTSBlockMode interface {
|
|
||||||
// BlockSize returns the mode's block size.
|
|
||||||
BlockSize() int
|
|
||||||
|
|
||||||
// Encrypt encrypts or decrypts a number of blocks. The length of
|
|
||||||
// src must be a multiple of the block size. Dst and src must overlap
|
|
||||||
// entirely or not at all.
|
|
||||||
//
|
|
||||||
Encrypt(dst, src []byte, tweak *[blockSize]byte)
|
|
||||||
|
|
||||||
// Decrypt decrypts a number of blocks. The length of
|
|
||||||
// src must be a multiple of the block size. Dst and src must overlap
|
|
||||||
// entirely or not at all.
|
|
||||||
//
|
|
||||||
Decrypt(dst, src []byte, tweak *[blockSize]byte)
|
|
||||||
|
|
||||||
// Encrypt encrypts or decrypts a number of blocks. The length of
|
|
||||||
// src must be a multiple of the block size. Dst and src must overlap
|
|
||||||
// entirely or not at all.
|
|
||||||
//
|
|
||||||
EncryptSector(dst, src []byte, sectorNum uint64)
|
|
||||||
|
|
||||||
// Decrypt decrypts a number of blocks. The length of
|
|
||||||
// src must be a multiple of the block size. Dst and src must overlap
|
|
||||||
// entirely or not at all.
|
|
||||||
//
|
|
||||||
DecryptSector(dst, src []byte, sectorNum uint64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cipher contains an expanded key structure. It is safe for concurrent use if
|
// Cipher contains an expanded key structure. It is safe for concurrent use if
|
||||||
// the underlying block cipher is safe for concurrent use.
|
// the underlying block cipher is safe for concurrent use.
|
||||||
type xts struct {
|
type xts struct {
|
||||||
k1, k2 _cipher.Block
|
b _cipher.Block
|
||||||
isGB bool // if true, follows GB/T 17964-2021
|
tweak [blockSize]byte
|
||||||
|
isGB bool // if true, follows GB/T 17964-2021
|
||||||
}
|
}
|
||||||
|
|
||||||
// blockSize is the block size that the underlying cipher must have. XTS is
|
// blockSize is the block size that the underlying cipher must have. XTS is
|
||||||
// only defined for 16-byte ciphers.
|
// only defined for 16-byte ciphers.
|
||||||
const blockSize = 16
|
const blockSize = 16
|
||||||
|
|
||||||
// NewGBXTS creates a Cipher given a function for creating the underlying
|
type xtsEncrypter xts
|
||||||
// block cipher (which must have a block size of 16 bytes). The key must be
|
|
||||||
// twice the length of the underlying cipher's key.
|
// xtsEncAble is an interface implemented by ciphers that have a specific
|
||||||
|
// optimized implementation of XTS encryption, like sm4.
|
||||||
|
// NewXTSEncrypter will check for this interface and return the specific
|
||||||
|
// BlockMode if found.
|
||||||
|
type xtsEncAble interface {
|
||||||
|
NewXTSEncrypter(encryptedTweak *[blockSize]byte, isGB bool) _cipher.BlockMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewXTSEncrypter creates a Cipher given a function for creating the underlying
|
||||||
|
// block cipher (which must have a block size of 16 bytes).
|
||||||
|
func NewXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) {
|
||||||
|
return newXTSEncrypter(cipherFunc, key, tweakKey, tweak, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewXTSEncrypterWithSector creates a Cipher given a function for creating the underlying
|
||||||
|
// block cipher (which must have a block size of 16 bytes) with sector number.
|
||||||
|
func NewXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (_cipher.BlockMode, error) {
|
||||||
|
tweak := make([]byte, blockSize)
|
||||||
|
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
|
||||||
|
return NewXTSEncrypter(cipherFunc, key, tweakKey, tweak)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGBXTSEncrypter creates a Cipher given a function for creating the underlying
|
||||||
|
// block cipher (which must have a block size of 16 bytes).
|
||||||
// It follows GB/T 17964-2021.
|
// It follows GB/T 17964-2021.
|
||||||
func NewGBXTS(cipherFunc CipherCreator, key []byte) (XTSBlockMode, error) {
|
func NewGBXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) {
|
||||||
return newXTS(cipherFunc, key, true)
|
return newXTSEncrypter(cipherFunc, key, tweakKey, tweak, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewXTS creates a Cipher given a function for creating the underlying
|
// NewGBXTSEncrypterWithSector creates a Cipher given a function for creating the underlying
|
||||||
// block cipher (which must have a block size of 16 bytes). The key must be
|
// block cipher (which must have a block size of 16 bytes) with sector number.
|
||||||
// twice the length of the underlying cipher's key.
|
// It follows GB/T 17964-2021.
|
||||||
func NewXTS(cipherFunc CipherCreator, key []byte) (XTSBlockMode, error) {
|
func NewGBXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (_cipher.BlockMode, error) {
|
||||||
return newXTS(cipherFunc, key, false)
|
tweak := make([]byte, blockSize)
|
||||||
|
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
|
||||||
|
return NewGBXTSEncrypter(cipherFunc, key, tweakKey, tweak)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newXTS(cipherFunc CipherCreator, key []byte, isGB bool) (*xts, error) {
|
func newXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte, isGB bool) (_cipher.BlockMode, error) {
|
||||||
k1, err := cipherFunc(key[:len(key)/2])
|
if len(tweak) != blockSize {
|
||||||
|
return nil, errors.New("xts: invalid tweak length")
|
||||||
|
}
|
||||||
|
|
||||||
|
k1, err := cipherFunc(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
k2, err := cipherFunc(key[len(key)/2:])
|
if k1.BlockSize() != blockSize {
|
||||||
|
return nil, errors.New("xts: cipher does not have a block size of 16")
|
||||||
|
}
|
||||||
|
|
||||||
|
k2, err := cipherFunc(tweakKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if xtsable, ok := k1.(xtsEncAble); ok {
|
||||||
|
var encryptedTweak [blockSize]byte
|
||||||
|
k2.Encrypt(encryptedTweak[:], tweak)
|
||||||
|
return xtsable.NewXTSEncrypter(&encryptedTweak, isGB), nil
|
||||||
|
}
|
||||||
|
|
||||||
c := &xts{
|
c := &xts{
|
||||||
k1,
|
b: k1,
|
||||||
k2,
|
isGB: isGB,
|
||||||
isGB,
|
|
||||||
}
|
}
|
||||||
|
k2.Encrypt(c.tweak[:], tweak)
|
||||||
if c.k1.BlockSize() != blockSize {
|
return (*xtsEncrypter)(c), nil
|
||||||
err = errors.New("xts: cipher does not have a block size of 16")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *xts) BlockSize() int {
|
type xtsDecrypter xts
|
||||||
|
|
||||||
|
// xtsDecAble is an interface implemented by ciphers that have a specific
|
||||||
|
// optimized implementation of XTS encryption, like sm4.
|
||||||
|
// NewXTSDecrypter will check for this interface and return the specific
|
||||||
|
// BlockMode if found.
|
||||||
|
type xtsDecAble interface {
|
||||||
|
NewXTSDecrypter(encryptedTweak *[blockSize]byte, isGB bool) _cipher.BlockMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewXTSDecrypter creates a Cipher given a function for creating the underlying
|
||||||
|
// block cipher (which must have a block size of 16 bytes) for decryption.
|
||||||
|
func NewXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) {
|
||||||
|
return newXTSDecrypter(cipherFunc, key, tweakKey, tweak, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewXTSDecrypterWithSector creates a Cipher given a function for creating the underlying
|
||||||
|
// block cipher (which must have a block size of 16 bytes) with sector number for decryption.
|
||||||
|
func NewXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (_cipher.BlockMode, error) {
|
||||||
|
tweak := make([]byte, blockSize)
|
||||||
|
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
|
||||||
|
return NewXTSDecrypter(cipherFunc, key, tweakKey, tweak)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGBXTSDecrypter creates a Cipher given a function for creating the underlying
|
||||||
|
// block cipher (which must have a block size of 16 bytes) for decryption.
|
||||||
|
// It follows GB/T 17964-2021.
|
||||||
|
func NewGBXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) {
|
||||||
|
return newXTSDecrypter(cipherFunc, key, tweakKey, tweak, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGBXTSDecrypterWithSector creates a Cipher given a function for creating the underlying
|
||||||
|
// block cipher (which must have a block size of 16 bytes) with sector number for decryption.
|
||||||
|
// It follows GB/T 17964-2021.
|
||||||
|
func NewGBXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (_cipher.BlockMode, error) {
|
||||||
|
tweak := make([]byte, blockSize)
|
||||||
|
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
|
||||||
|
return NewGBXTSDecrypter(cipherFunc, key, tweakKey, tweak)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte, isGB bool) (_cipher.BlockMode, error) {
|
||||||
|
if len(tweak) != blockSize {
|
||||||
|
return nil, errors.New("xts: invalid tweak length")
|
||||||
|
}
|
||||||
|
|
||||||
|
k1, err := cipherFunc(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if k1.BlockSize() != blockSize {
|
||||||
|
return nil, errors.New("xts: cipher does not have a block size of 16")
|
||||||
|
}
|
||||||
|
|
||||||
|
k2, err := cipherFunc(tweakKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if xtsable, ok := k1.(xtsDecAble); ok {
|
||||||
|
var encryptedTweak [blockSize]byte
|
||||||
|
k2.Encrypt(encryptedTweak[:], tweak)
|
||||||
|
return xtsable.NewXTSDecrypter(&encryptedTweak, isGB), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &xts{
|
||||||
|
b: k1,
|
||||||
|
isGB: isGB,
|
||||||
|
}
|
||||||
|
k2.Encrypt(c.tweak[:], tweak)
|
||||||
|
return (*xtsDecrypter)(c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xtsEncrypter) BlockSize() int {
|
||||||
return blockSize
|
return blockSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *xts) fillTweak(tweak *[blockSize]byte, sectorNum uint64) {
|
// CryptBlocks encrypts a sector of plaintext and puts the result into ciphertext.
|
||||||
for i := range tweak {
|
|
||||||
tweak[i] = 0
|
|
||||||
}
|
|
||||||
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt encrypts a sector of plaintext and puts the result into ciphertext.
|
|
||||||
// Plaintext and ciphertext must overlap entirely or not at all.
|
// Plaintext and ciphertext must overlap entirely or not at all.
|
||||||
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
|
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
|
||||||
func (c *xts) Encrypt(ciphertext, plaintext []byte, tweak *[blockSize]byte) {
|
func (c *xtsEncrypter) CryptBlocks(ciphertext, plaintext []byte) {
|
||||||
if tweak == nil {
|
|
||||||
panic("xts: invalid tweak")
|
|
||||||
}
|
|
||||||
if len(ciphertext) < len(plaintext) {
|
if len(ciphertext) < len(plaintext) {
|
||||||
panic("xts: ciphertext is smaller than plaintext")
|
panic("xts: ciphertext is smaller than plaintext")
|
||||||
}
|
}
|
||||||
@ -125,18 +193,16 @@ func (c *xts) Encrypt(ciphertext, plaintext []byte, tweak *[blockSize]byte) {
|
|||||||
panic("xts: invalid buffer overlap")
|
panic("xts: invalid buffer overlap")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.k2.Encrypt(tweak[:], tweak[:])
|
|
||||||
|
|
||||||
lastCiphertext := ciphertext
|
lastCiphertext := ciphertext
|
||||||
|
|
||||||
if concCipher, ok := c.k1.(concurrentBlocks); ok {
|
if concCipher, ok := c.b.(concurrentBlocks); ok {
|
||||||
batchSize := concCipher.Concurrency() * blockSize
|
batchSize := concCipher.Concurrency() * blockSize
|
||||||
var tweaks []byte = make([]byte, batchSize)
|
var tweaks []byte = make([]byte, batchSize)
|
||||||
|
|
||||||
for len(plaintext) >= batchSize {
|
for len(plaintext) >= batchSize {
|
||||||
for i := 0; i < concCipher.Concurrency(); i++ {
|
for i := 0; i < concCipher.Concurrency(); i++ {
|
||||||
copy(tweaks[blockSize*i:], tweak[:])
|
copy(tweaks[blockSize*i:], c.tweak[:])
|
||||||
mul2(tweak, c.isGB)
|
mul2(&c.tweak, c.isGB)
|
||||||
}
|
}
|
||||||
subtle.XORBytes(ciphertext, plaintext, tweaks)
|
subtle.XORBytes(ciphertext, plaintext, tweaks)
|
||||||
concCipher.EncryptBlocks(ciphertext, ciphertext)
|
concCipher.EncryptBlocks(ciphertext, ciphertext)
|
||||||
@ -147,13 +213,13 @@ func (c *xts) Encrypt(ciphertext, plaintext []byte, tweak *[blockSize]byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for len(plaintext) >= blockSize {
|
for len(plaintext) >= blockSize {
|
||||||
subtle.XORBytes(ciphertext, plaintext, tweak[:])
|
subtle.XORBytes(ciphertext, plaintext, c.tweak[:])
|
||||||
c.k1.Encrypt(ciphertext, ciphertext)
|
c.b.Encrypt(ciphertext, ciphertext)
|
||||||
subtle.XORBytes(ciphertext, ciphertext, tweak[:])
|
subtle.XORBytes(ciphertext, ciphertext, c.tweak[:])
|
||||||
plaintext = plaintext[blockSize:]
|
plaintext = plaintext[blockSize:]
|
||||||
lastCiphertext = ciphertext
|
lastCiphertext = ciphertext
|
||||||
ciphertext = ciphertext[blockSize:]
|
ciphertext = ciphertext[blockSize:]
|
||||||
mul2(tweak, c.isGB)
|
mul2(&c.tweak, c.isGB)
|
||||||
}
|
}
|
||||||
// is there a final partial block to handle?
|
// is there a final partial block to handle?
|
||||||
if remain := len(plaintext); remain > 0 {
|
if remain := len(plaintext); remain > 0 {
|
||||||
@ -165,30 +231,22 @@ func (c *xts) Encrypt(ciphertext, plaintext []byte, tweak *[blockSize]byte) {
|
|||||||
//Steal ciphertext to complete the block
|
//Steal ciphertext to complete the block
|
||||||
copy(x[remain:], lastCiphertext[remain:blockSize])
|
copy(x[remain:], lastCiphertext[remain:blockSize])
|
||||||
//Merge the tweak into the input block
|
//Merge the tweak into the input block
|
||||||
subtle.XORBytes(x[:], x[:], tweak[:])
|
subtle.XORBytes(x[:], x[:], c.tweak[:])
|
||||||
//Encrypt the final block using K1
|
//Encrypt the final block using K1
|
||||||
c.k1.Encrypt(x[:], x[:])
|
c.b.Encrypt(x[:], x[:])
|
||||||
//Merge the tweak into the output block
|
//Merge the tweak into the output block
|
||||||
subtle.XORBytes(lastCiphertext, x[:], tweak[:])
|
subtle.XORBytes(lastCiphertext, x[:], c.tweak[:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt encrypts a sector of plaintext and puts the result into ciphertext.
|
func (c *xtsDecrypter) BlockSize() int {
|
||||||
// Plaintext and ciphertext must overlap entirely or not at all.
|
return blockSize
|
||||||
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
|
|
||||||
func (c *xts) EncryptSector(ciphertext, plaintext []byte, sectorNum uint64) {
|
|
||||||
var tweak [blockSize]byte
|
|
||||||
c.fillTweak(&tweak, sectorNum)
|
|
||||||
c.Encrypt(ciphertext, plaintext, &tweak)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt decrypts a sector of ciphertext and puts the result into plaintext.
|
// CryptBlocks decrypts a sector of ciphertext and puts the result into plaintext.
|
||||||
// Plaintext and ciphertext must overlap entirely or not at all.
|
// Plaintext and ciphertext must overlap entirely or not at all.
|
||||||
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
|
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
|
||||||
func (c *xts) Decrypt(plaintext, ciphertext []byte, tweak *[blockSize]byte) {
|
func (c *xtsDecrypter) CryptBlocks(plaintext, ciphertext []byte) {
|
||||||
if tweak == nil {
|
|
||||||
panic("xts: invalid tweak")
|
|
||||||
}
|
|
||||||
if len(plaintext) < len(ciphertext) {
|
if len(plaintext) < len(ciphertext) {
|
||||||
panic("xts: plaintext is smaller than ciphertext")
|
panic("xts: plaintext is smaller than ciphertext")
|
||||||
}
|
}
|
||||||
@ -199,16 +257,14 @@ func (c *xts) Decrypt(plaintext, ciphertext []byte, tweak *[blockSize]byte) {
|
|||||||
panic("xts: invalid buffer overlap")
|
panic("xts: invalid buffer overlap")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.k2.Encrypt(tweak[:], tweak[:])
|
if concCipher, ok := c.b.(concurrentBlocks); ok {
|
||||||
|
|
||||||
if concCipher, ok := c.k1.(concurrentBlocks); ok {
|
|
||||||
batchSize := concCipher.Concurrency() * blockSize
|
batchSize := concCipher.Concurrency() * blockSize
|
||||||
var tweaks []byte = make([]byte, batchSize)
|
var tweaks []byte = make([]byte, batchSize)
|
||||||
|
|
||||||
for len(ciphertext) >= batchSize {
|
for len(ciphertext) >= batchSize {
|
||||||
for i := 0; i < concCipher.Concurrency(); i++ {
|
for i := 0; i < concCipher.Concurrency(); i++ {
|
||||||
copy(tweaks[blockSize*i:], tweak[:])
|
copy(tweaks[blockSize*i:], c.tweak[:])
|
||||||
mul2(tweak, c.isGB)
|
mul2(&c.tweak, c.isGB)
|
||||||
}
|
}
|
||||||
subtle.XORBytes(plaintext, ciphertext, tweaks)
|
subtle.XORBytes(plaintext, ciphertext, tweaks)
|
||||||
concCipher.DecryptBlocks(plaintext, plaintext)
|
concCipher.DecryptBlocks(plaintext, plaintext)
|
||||||
@ -219,23 +275,23 @@ func (c *xts) Decrypt(plaintext, ciphertext []byte, tweak *[blockSize]byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for len(ciphertext) >= 2*blockSize {
|
for len(ciphertext) >= 2*blockSize {
|
||||||
subtle.XORBytes(plaintext, ciphertext, tweak[:])
|
subtle.XORBytes(plaintext, ciphertext, c.tweak[:])
|
||||||
c.k1.Decrypt(plaintext, plaintext)
|
c.b.Decrypt(plaintext, plaintext)
|
||||||
subtle.XORBytes(plaintext, plaintext, tweak[:])
|
subtle.XORBytes(plaintext, plaintext, c.tweak[:])
|
||||||
plaintext = plaintext[blockSize:]
|
plaintext = plaintext[blockSize:]
|
||||||
ciphertext = ciphertext[blockSize:]
|
ciphertext = ciphertext[blockSize:]
|
||||||
|
|
||||||
mul2(tweak, c.isGB)
|
mul2(&c.tweak, c.isGB)
|
||||||
}
|
}
|
||||||
|
|
||||||
if remain := len(ciphertext); remain >= blockSize {
|
if remain := len(ciphertext); remain >= blockSize {
|
||||||
var x [blockSize]byte
|
var x [blockSize]byte
|
||||||
if remain > blockSize {
|
if remain > blockSize {
|
||||||
var tt [blockSize]byte
|
var tt [blockSize]byte
|
||||||
copy(tt[:], tweak[:])
|
copy(tt[:], c.tweak[:])
|
||||||
mul2(&tt, c.isGB)
|
mul2(&tt, c.isGB)
|
||||||
subtle.XORBytes(x[:], ciphertext, tt[:])
|
subtle.XORBytes(x[:], ciphertext, tt[:])
|
||||||
c.k1.Decrypt(x[:], x[:])
|
c.b.Decrypt(x[:], x[:])
|
||||||
subtle.XORBytes(plaintext, x[:], tt[:])
|
subtle.XORBytes(plaintext, x[:], tt[:])
|
||||||
|
|
||||||
//Retrieve the length of the final block
|
//Retrieve the length of the final block
|
||||||
@ -251,21 +307,12 @@ func (c *xts) Decrypt(plaintext, ciphertext []byte, tweak *[blockSize]byte) {
|
|||||||
//The last block contains exactly 128 bits
|
//The last block contains exactly 128 bits
|
||||||
copy(x[:], ciphertext)
|
copy(x[:], ciphertext)
|
||||||
}
|
}
|
||||||
subtle.XORBytes(x[:], x[:], tweak[:])
|
subtle.XORBytes(x[:], x[:], c.tweak[:])
|
||||||
c.k1.Decrypt(x[:], x[:])
|
c.b.Decrypt(x[:], x[:])
|
||||||
subtle.XORBytes(plaintext, x[:], tweak[:])
|
subtle.XORBytes(plaintext, x[:], c.tweak[:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt decrypts a sector of ciphertext and puts the result into plaintext.
|
|
||||||
// Plaintext and ciphertext must overlap entirely or not at all.
|
|
||||||
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
|
|
||||||
func (c *xts) DecryptSector(plaintext, ciphertext []byte, sectorNum uint64) {
|
|
||||||
var tweak [blockSize]byte
|
|
||||||
c.fillTweak(&tweak, sectorNum)
|
|
||||||
c.Decrypt(plaintext, ciphertext, &tweak)
|
|
||||||
}
|
|
||||||
|
|
||||||
// mul2 multiplies tweak by 2 in GF(2¹²⁸) with an irreducible polynomial of
|
// mul2 multiplies tweak by 2 in GF(2¹²⁸) with an irreducible polynomial of
|
||||||
// x¹²⁸ + x⁷ + x² + x + 1.
|
// x¹²⁸ + x⁷ + x² + x + 1.
|
||||||
func mul2(tweak *[blockSize]byte, isGB bool) {
|
func mul2(tweak *[blockSize]byte, isGB bool) {
|
||||||
|
@ -69,15 +69,22 @@ func fromHex(s string) []byte {
|
|||||||
|
|
||||||
func TestXTS(t *testing.T) {
|
func TestXTS(t *testing.T) {
|
||||||
for i, test := range xtsTestVectors {
|
for i, test := range xtsTestVectors {
|
||||||
c, err := cipher.NewXTS(sm4.NewCipher, fromHex(test.key))
|
key := fromHex(test.key)
|
||||||
|
|
||||||
|
encrypter, err := cipher.NewXTSEncrypterWithSector(sm4.NewCipher, key[:len(key)/2], key[len(key)/2:], test.sector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("#%d: failed to create cipher: %s", i, err)
|
t.Errorf("#%d: failed to create encrypter: %s", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
decrypter, err := cipher.NewXTSDecrypterWithSector(sm4.NewCipher, key[:len(key)/2], key[len(key)/2:], test.sector)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("#%d: failed to create decrypter: %s", i, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
plaintext := fromHex(test.plaintext)
|
plaintext := fromHex(test.plaintext)
|
||||||
ciphertext := make([]byte, len(plaintext))
|
ciphertext := make([]byte, len(plaintext))
|
||||||
|
|
||||||
c.EncryptSector(ciphertext, plaintext, test.sector)
|
encrypter.CryptBlocks(ciphertext, plaintext)
|
||||||
expectedCiphertext := fromHex(test.ciphertext)
|
expectedCiphertext := fromHex(test.ciphertext)
|
||||||
if !bytes.Equal(ciphertext, expectedCiphertext) {
|
if !bytes.Equal(ciphertext, expectedCiphertext) {
|
||||||
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
|
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
|
||||||
@ -85,7 +92,7 @@ func TestXTS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
decrypted := make([]byte, len(ciphertext))
|
decrypted := make([]byte, len(ciphertext))
|
||||||
c.DecryptSector(decrypted, ciphertext, test.sector)
|
decrypter.CryptBlocks(decrypted, ciphertext)
|
||||||
if !bytes.Equal(decrypted, plaintext) {
|
if !bytes.Equal(decrypted, plaintext) {
|
||||||
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
|
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
|
||||||
}
|
}
|
||||||
@ -109,21 +116,22 @@ var xtsGBTestVectors = []struct {
|
|||||||
|
|
||||||
func TestXTS_GB(t *testing.T) {
|
func TestXTS_GB(t *testing.T) {
|
||||||
for i, test := range xtsGBTestVectors {
|
for i, test := range xtsGBTestVectors {
|
||||||
c, err := cipher.NewGBXTS(sm4.NewCipher, fromHex(test.key))
|
key := fromHex(test.key)
|
||||||
|
tweak := fromHex(test.tweak)
|
||||||
|
encrypter, err := cipher.NewGBXTSEncrypter(sm4.NewCipher, key[:len(key)/2], key[len(key)/2:], tweak)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("#%d: failed to create cipher: %s", i, err)
|
t.Errorf("#%d: failed to create encrypter: %s", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
decrypter, err := cipher.NewGBXTSDecrypter(sm4.NewCipher, key[:len(key)/2], key[len(key)/2:], tweak)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("#%d: failed to create decrypter: %s", i, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
plaintext := fromHex(test.plaintext)
|
plaintext := fromHex(test.plaintext)
|
||||||
ciphertext := make([]byte, len(plaintext))
|
ciphertext := make([]byte, len(plaintext))
|
||||||
var tweak1 [16]byte
|
|
||||||
var tweak2 [16]byte
|
|
||||||
|
|
||||||
tweak := fromHex(test.tweak)
|
encrypter.CryptBlocks(ciphertext, plaintext)
|
||||||
copy(tweak1[:], tweak)
|
|
||||||
copy(tweak2[:], tweak)
|
|
||||||
|
|
||||||
c.Encrypt(ciphertext, plaintext, &tweak1)
|
|
||||||
expectedCiphertext := fromHex(test.ciphertext)
|
expectedCiphertext := fromHex(test.ciphertext)
|
||||||
if !bytes.Equal(ciphertext, expectedCiphertext) {
|
if !bytes.Equal(ciphertext, expectedCiphertext) {
|
||||||
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
|
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
|
||||||
@ -131,7 +139,7 @@ func TestXTS_GB(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
decrypted := make([]byte, len(ciphertext))
|
decrypted := make([]byte, len(ciphertext))
|
||||||
c.Decrypt(decrypted, ciphertext, &tweak2)
|
decrypter.CryptBlocks(decrypted, ciphertext)
|
||||||
if !bytes.Equal(decrypted, plaintext) {
|
if !bytes.Equal(decrypted, plaintext) {
|
||||||
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
|
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
|
||||||
}
|
}
|
||||||
|
@ -66,15 +66,22 @@ var xtsAesTestVectors = []struct {
|
|||||||
|
|
||||||
func TestXTSWithAES(t *testing.T) {
|
func TestXTSWithAES(t *testing.T) {
|
||||||
for i, test := range xtsAesTestVectors {
|
for i, test := range xtsAesTestVectors {
|
||||||
c, err := cipher.NewXTS(aes.NewCipher, fromHex(test.key))
|
key := fromHex(test.key)
|
||||||
|
|
||||||
|
encrypter, err := cipher.NewXTSEncrypterWithSector(aes.NewCipher, key[:len(key)/2], key[len(key)/2:], test.sector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("#%d: failed to create cipher: %s", i, err)
|
t.Errorf("#%d: failed to create encrypter: %s", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
decrypter, err := cipher.NewXTSDecrypterWithSector(aes.NewCipher, key[:len(key)/2], key[len(key)/2:], test.sector)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("#%d: failed to create decrypter: %s", i, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
plaintext := fromHex(test.plaintext)
|
plaintext := fromHex(test.plaintext)
|
||||||
ciphertext := make([]byte, len(plaintext))
|
ciphertext := make([]byte, len(plaintext))
|
||||||
|
|
||||||
c.EncryptSector(ciphertext, plaintext, test.sector)
|
encrypter.CryptBlocks(ciphertext, plaintext)
|
||||||
expectedCiphertext := fromHex(test.ciphertext)
|
expectedCiphertext := fromHex(test.ciphertext)
|
||||||
if !bytes.Equal(ciphertext, expectedCiphertext) {
|
if !bytes.Equal(ciphertext, expectedCiphertext) {
|
||||||
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
|
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
|
||||||
@ -82,7 +89,7 @@ func TestXTSWithAES(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
decrypted := make([]byte, len(ciphertext))
|
decrypted := make([]byte, len(ciphertext))
|
||||||
c.DecryptSector(decrypted, ciphertext, test.sector)
|
decrypter.CryptBlocks(decrypted, ciphertext)
|
||||||
if !bytes.Equal(decrypted, plaintext) {
|
if !bytes.Equal(decrypted, plaintext) {
|
||||||
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
|
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
|
||||||
}
|
}
|
||||||
@ -90,17 +97,22 @@ func TestXTSWithAES(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestShorterCiphertext(t *testing.T) {
|
func TestShorterCiphertext(t *testing.T) {
|
||||||
c, err := cipher.NewXTS(aes.NewCipher, make([]byte, 32))
|
encrypter, err := cipher.NewXTSEncrypterWithSector(aes.NewCipher, make([]byte, 16), make([]byte, 16), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewCipher failed: %s", err)
|
t.Fatalf("NewXTSEncrypterWithSector failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypter, err := cipher.NewXTSDecrypterWithSector(aes.NewCipher, make([]byte, 16), make([]byte, 16), 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewXTSDecrypterWithSector failed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
plaintext := make([]byte, 32)
|
plaintext := make([]byte, 32)
|
||||||
encrypted := make([]byte, 48)
|
encrypted := make([]byte, 48)
|
||||||
decrypted := make([]byte, 48)
|
decrypted := make([]byte, 48)
|
||||||
|
|
||||||
c.EncryptSector(encrypted, plaintext, 0)
|
encrypter.CryptBlocks(encrypted, plaintext)
|
||||||
c.DecryptSector(decrypted, encrypted[:len(plaintext)], 0)
|
decrypter.CryptBlocks(decrypted, encrypted[:len(plaintext)])
|
||||||
|
|
||||||
if !bytes.Equal(plaintext, decrypted[:len(plaintext)]) {
|
if !bytes.Equal(plaintext, decrypted[:len(plaintext)]) {
|
||||||
t.Errorf("En/Decryption is not inverse")
|
t.Errorf("En/Decryption is not inverse")
|
||||||
|
20
cipher/xts_tweak_test.go
Normal file
20
cipher/xts_tweak_test.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package cipher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkDoubleTweak(b *testing.B) {
|
||||||
|
var tweak [16]byte
|
||||||
|
block, err := aes.NewCipher(make([]byte, 16))
|
||||||
|
if err != nil {
|
||||||
|
b.Failed()
|
||||||
|
}
|
||||||
|
block.Encrypt(tweak[:], tweak[:])
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
mul2(&tweak, false)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user