gmsm/cipher/xts.go

355 lines
12 KiB
Go
Raw Normal View History

2022-01-21 11:24:10 +08:00
package cipher
import (
"crypto/cipher"
"crypto/subtle"
2022-01-21 11:24:10 +08:00
"errors"
2022-08-18 14:49:35 +08:00
"github.com/emmansun/gmsm/internal/alias"
2024-11-21 14:32:32 +08:00
"github.com/emmansun/gmsm/internal/byteorder"
2022-01-21 11:24:10 +08:00
)
const GF128_FDBK byte = 0x87
type CipherCreator func([]byte) (cipher.Block, error)
2022-01-21 11:24:10 +08:00
type concurrentBlocks interface {
Concurrency() int
EncryptBlocks(dst, src []byte)
DecryptBlocks(dst, src []byte)
}
// Cipher contains an expanded key structure. It is unsafe for concurrent use.
2022-01-21 11:24:10 +08:00
type xts struct {
b cipher.Block
2023-08-17 12:48:53 +08:00
tweak [blockSize]byte
isGB bool // if true, follows GB/T 17964-2021
2022-01-21 11:24:10 +08:00
}
// blockSize is the block size that the underlying cipher must have. XTS is
// only defined for 16-byte ciphers.
const blockSize = 16
2023-08-17 12:48:53 +08:00
type xtsEncrypter xts
// 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
2023-08-17 12:48:53 +08:00
}
// 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) {
2023-08-17 12:48:53 +08:00
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) {
2023-08-17 12:48:53 +08:00
tweak := make([]byte, blockSize)
2024-11-21 14:32:32 +08:00
byteorder.LEPutUint64(tweak[:8], sectorNum)
2023-08-17 12:48:53 +08:00
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).
2023-08-08 17:26:08 +08:00
// It follows GB/T 17964-2021.
func NewGBXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
2023-08-17 12:48:53 +08:00
return newXTSEncrypter(cipherFunc, key, tweakKey, tweak, true)
2022-01-21 11:24:10 +08:00
}
2023-08-17 12:48:53 +08:00
// NewGBXTSEncrypterWithSector creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) with sector number.
// It follows GB/T 17964-2021.
func NewGBXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
2023-08-17 12:48:53 +08:00
tweak := make([]byte, blockSize)
2024-11-21 14:32:32 +08:00
byteorder.LEPutUint64(tweak[:8], sectorNum)
2023-08-17 12:48:53 +08:00
return NewGBXTSEncrypter(cipherFunc, key, tweakKey, tweak)
2023-08-08 17:26:08 +08:00
}
func newXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte, isGB bool) (cipher.BlockMode, error) {
2023-08-17 12:48:53 +08:00
if len(tweak) != blockSize {
2023-12-08 17:42:16 +08:00
return nil, errors.New("cipher: invalid tweak length")
2023-08-17 12:48:53 +08:00
}
k1, err := cipherFunc(key)
2022-01-21 11:24:10 +08:00
if err != nil {
return nil, err
}
2023-08-17 12:48:53 +08:00
if k1.BlockSize() != blockSize {
2023-12-08 17:42:16 +08:00
return nil, errors.New("cipher: cipher does not have a block size of 16")
2023-08-17 12:48:53 +08:00
}
k2, err := cipherFunc(tweakKey)
2023-08-08 17:26:08 +08:00
if err != nil {
return nil, err
}
2023-08-17 12:48:53 +08:00
if xtsable, ok := k1.(xtsEncAble); ok {
var encryptedTweak [blockSize]byte
k2.Encrypt(encryptedTweak[:], tweak)
return xtsable.NewXTSEncrypter(&encryptedTweak, isGB), nil
2022-01-21 11:24:10 +08:00
}
2023-08-17 12:48:53 +08:00
c := &xts{
b: k1,
isGB: isGB,
2022-01-21 11:24:10 +08:00
}
2023-08-17 12:48:53 +08:00
k2.Encrypt(c.tweak[:], tweak)
return (*xtsEncrypter)(c), nil
2022-01-21 11:24:10 +08:00
}
2023-08-17 12:48:53 +08:00
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
2022-01-21 11:24:10 +08:00
}
2023-08-17 12:48:53 +08:00
// 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) {
2023-08-17 12:48:53 +08:00
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) {
2023-08-17 12:48:53 +08:00
tweak := make([]byte, blockSize)
2024-11-21 14:32:32 +08:00
byteorder.LEPutUint64(tweak[:8], sectorNum)
2023-08-17 12:48:53 +08:00
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) {
2023-08-17 12:48:53 +08:00
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) {
2023-08-17 12:48:53 +08:00
tweak := make([]byte, blockSize)
2024-11-21 14:32:32 +08:00
byteorder.LEPutUint64(tweak[:8], sectorNum)
2023-08-17 12:48:53 +08:00
return NewGBXTSDecrypter(cipherFunc, key, tweakKey, tweak)
2023-08-08 17:26:08 +08:00
}
func newXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte, isGB bool) (cipher.BlockMode, error) {
2023-08-17 12:48:53 +08:00
if len(tweak) != blockSize {
2023-12-08 17:42:16 +08:00
return nil, errors.New("cipher: invalid tweak length")
2023-08-17 12:48:53 +08:00
}
k1, err := cipherFunc(key)
if err != nil {
return nil, err
}
if k1.BlockSize() != blockSize {
2023-12-08 17:42:16 +08:00
return nil, errors.New("cipher: cipher does not have a block size of 16")
2023-08-17 12:48:53 +08:00
}
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
}
// CryptBlocks encrypts a sector of plaintext and puts the result into ciphertext.
2022-01-21 11:24:10 +08:00
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
2023-08-17 12:48:53 +08:00
func (c *xtsEncrypter) CryptBlocks(ciphertext, plaintext []byte) {
2022-01-21 11:24:10 +08:00
if len(ciphertext) < len(plaintext) {
2023-12-08 17:42:16 +08:00
panic("cipher: ciphertext is smaller than plaintext")
2022-01-21 11:24:10 +08:00
}
if len(plaintext) < blockSize {
2023-12-08 17:42:16 +08:00
panic("cipher: plaintext length is smaller than the block size")
2022-01-21 11:24:10 +08:00
}
2022-08-18 14:49:35 +08:00
if alias.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
2023-12-08 17:42:16 +08:00
panic("cipher: invalid buffer overlap")
2022-01-21 11:24:10 +08:00
}
lastCiphertext := ciphertext
2023-08-17 12:48:53 +08:00
if concCipher, ok := c.b.(concurrentBlocks); ok {
2022-01-21 11:24:10 +08:00
batchSize := concCipher.Concurrency() * blockSize
var tweaks []byte = make([]byte, batchSize)
for len(plaintext) >= batchSize {
doubleTweaks(&c.tweak, tweaks, c.isGB)
2022-08-18 14:49:35 +08:00
subtle.XORBytes(ciphertext, plaintext, tweaks)
2022-01-21 11:24:10 +08:00
concCipher.EncryptBlocks(ciphertext, ciphertext)
2022-08-18 14:49:35 +08:00
subtle.XORBytes(ciphertext, ciphertext, tweaks)
2022-01-21 11:24:10 +08:00
plaintext = plaintext[batchSize:]
lastCiphertext = ciphertext[batchSize-blockSize:]
ciphertext = ciphertext[batchSize:]
}
}
2022-01-21 11:24:10 +08:00
for len(plaintext) >= blockSize {
2023-08-17 12:48:53 +08:00
subtle.XORBytes(ciphertext, plaintext, c.tweak[:])
c.b.Encrypt(ciphertext, ciphertext)
subtle.XORBytes(ciphertext, ciphertext, c.tweak[:])
2022-01-21 11:24:10 +08:00
plaintext = plaintext[blockSize:]
lastCiphertext = ciphertext
ciphertext = ciphertext[blockSize:]
2023-08-17 12:48:53 +08:00
mul2(&c.tweak, c.isGB)
2022-01-21 11:24:10 +08:00
}
// is there a final partial block to handle?
if remain := len(plaintext); remain > 0 {
var x [blockSize]byte
//Copy the final plaintext bytes
copy(x[:], plaintext)
//Steal ciphertext to complete the block
copy(x[remain:], lastCiphertext[remain:blockSize])
2023-08-23 08:30:40 +08:00
//Copy the final ciphertext bytes
copy(ciphertext, lastCiphertext[:remain])
2022-01-21 11:24:10 +08:00
//Merge the tweak into the input block
2023-08-17 12:48:53 +08:00
subtle.XORBytes(x[:], x[:], c.tweak[:])
2022-01-21 11:24:10 +08:00
//Encrypt the final block using K1
2023-08-17 12:48:53 +08:00
c.b.Encrypt(x[:], x[:])
2022-01-21 11:24:10 +08:00
//Merge the tweak into the output block
2023-08-17 12:48:53 +08:00
subtle.XORBytes(lastCiphertext, x[:], c.tweak[:])
2022-01-21 11:24:10 +08:00
}
2023-08-08 17:26:08 +08:00
}
2023-08-17 12:48:53 +08:00
func (c *xtsDecrypter) BlockSize() int {
return blockSize
2022-01-21 11:24:10 +08:00
}
2023-08-17 12:48:53 +08:00
// CryptBlocks decrypts a sector of ciphertext and puts the result into plaintext.
2022-01-21 11:24:10 +08:00
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
2023-08-17 12:48:53 +08:00
func (c *xtsDecrypter) CryptBlocks(plaintext, ciphertext []byte) {
2022-01-21 11:24:10 +08:00
if len(plaintext) < len(ciphertext) {
2023-12-08 17:42:16 +08:00
panic("cipher: plaintext is smaller than ciphertext")
2022-01-21 11:24:10 +08:00
}
if len(ciphertext) < blockSize {
2023-12-08 17:42:16 +08:00
panic("cipher: ciphertext length is smaller than the block size")
2022-01-21 11:24:10 +08:00
}
2022-08-18 14:49:35 +08:00
if alias.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
2023-12-08 17:42:16 +08:00
panic("cipher: invalid buffer overlap")
2022-01-21 11:24:10 +08:00
}
2023-08-17 12:48:53 +08:00
if concCipher, ok := c.b.(concurrentBlocks); ok {
2022-01-21 11:24:10 +08:00
batchSize := concCipher.Concurrency() * blockSize
var tweaks []byte = make([]byte, batchSize)
for len(ciphertext) >= batchSize {
doubleTweaks(&c.tweak, tweaks, c.isGB)
2022-08-18 14:49:35 +08:00
subtle.XORBytes(plaintext, ciphertext, tweaks)
2022-01-21 11:24:10 +08:00
concCipher.DecryptBlocks(plaintext, plaintext)
2022-08-18 14:49:35 +08:00
subtle.XORBytes(plaintext, plaintext, tweaks)
2022-01-21 11:24:10 +08:00
plaintext = plaintext[batchSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(ciphertext) >= 2*blockSize {
2023-08-17 12:48:53 +08:00
subtle.XORBytes(plaintext, ciphertext, c.tweak[:])
c.b.Decrypt(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, c.tweak[:])
2022-01-21 11:24:10 +08:00
plaintext = plaintext[blockSize:]
ciphertext = ciphertext[blockSize:]
2023-08-17 12:48:53 +08:00
mul2(&c.tweak, c.isGB)
2022-01-21 11:24:10 +08:00
}
if remain := len(ciphertext); remain >= blockSize {
var x [blockSize]byte
if remain > blockSize {
var tt [blockSize]byte
2023-08-17 12:48:53 +08:00
copy(tt[:], c.tweak[:])
2023-08-08 17:26:08 +08:00
mul2(&tt, c.isGB)
2022-08-18 14:49:35 +08:00
subtle.XORBytes(x[:], ciphertext, tt[:])
2023-08-17 12:48:53 +08:00
c.b.Decrypt(x[:], x[:])
2022-08-18 14:49:35 +08:00
subtle.XORBytes(plaintext, x[:], tt[:])
2022-01-21 11:24:10 +08:00
//Retrieve the length of the final block
remain -= blockSize
//Copy the final ciphertext bytes
copy(x[:], ciphertext[blockSize:])
//Steal ciphertext to complete the block
copy(x[remain:], plaintext[remain:blockSize])
2023-08-23 08:30:40 +08:00
//Copy the final plaintext bytes
copy(plaintext[blockSize:], plaintext)
2023-08-24 11:47:06 +08:00
subtle.XORBytes(x[:], x[:], c.tweak[:])
c.b.Decrypt(x[:], x[:])
subtle.XORBytes(plaintext, x[:], c.tweak[:])
2022-01-21 11:24:10 +08:00
} else {
//The last block contains exactly 128 bits
2023-08-24 11:47:06 +08:00
subtle.XORBytes(plaintext, ciphertext, c.tweak[:])
c.b.Decrypt(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, c.tweak[:])
// Maybe there are still ciphertext
mul2(&c.tweak, c.isGB)
2022-01-21 11:24:10 +08:00
}
2023-08-24 11:47:06 +08:00
2022-01-21 11:24:10 +08:00
}
2023-08-08 17:26:08 +08:00
}
2022-01-21 11:24:10 +08:00
// mul2Generic multiplies tweak by 2 in GF(2¹²⁸) with an irreducible polynomial of
2022-01-21 11:24:10 +08:00
// x¹²⁸ + x⁷ + x² + x + 1.
func mul2Generic(tweak *[blockSize]byte, isGB bool) {
2022-01-21 11:24:10 +08:00
var carryIn byte
2023-08-08 17:26:08 +08:00
if !isGB {
2023-12-01 15:51:15 +08:00
// the coefficient of x⁰ can be obtained by tweak[0] & 1
// the coefficient of x⁷ can be obtained by tweak[0] >> 7
// the coefficient of x¹²⁰ can be obtained by tweak[15] & 1
// the coefficient of x¹²⁷ can be obtained by tweak[15] >> 7
2023-08-08 17:26:08 +08:00
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] ^= GF128_FDBK // 1<<7 | 1<<2 | 1<<1 | 1
}
} else {
2024-11-21 14:32:32 +08:00
// GB/T 17964-2021,
2023-12-01 15:51:15 +08:00
// the coefficient of x⁰ can be obtained by tweak[0] >> 7
// the coefficient of x⁷ can be obtained by tweak[0] & 1
// the coefficient of x¹²⁰ can be obtained by tweak[15] >> 7
// the coefficient of x¹²⁷ can be obtained by tweak[15] & 1
2023-08-08 17:26:08 +08:00
for j := range tweak {
carryOut := (tweak[j] << 7) & 0x80
tweak[j] = (tweak[j] >> 1) + carryIn
carryIn = carryOut
}
if carryIn != 0 {
tweak[0] ^= 0xE1 // 1<<7 | 1<<6 | 1<<5 | 1
}
2022-01-21 11:24:10 +08:00
}
}