mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-22 02:06:18 +08:00
351 lines
12 KiB
Go
351 lines
12 KiB
Go
package cipher
|
|
|
|
import (
|
|
_cipher "crypto/cipher"
|
|
"encoding/binary"
|
|
"errors"
|
|
|
|
"github.com/emmansun/gmsm/internal/alias"
|
|
"github.com/emmansun/gmsm/internal/subtle"
|
|
)
|
|
|
|
const GF128_FDBK byte = 0x87
|
|
|
|
type CipherCreator func([]byte) (_cipher.Block, error)
|
|
|
|
type concurrentBlocks interface {
|
|
Concurrency() int
|
|
EncryptBlocks(dst, src []byte)
|
|
DecryptBlocks(dst, src []byte)
|
|
}
|
|
|
|
// Cipher contains an expanded key structure. It is safe for concurrent use if
|
|
// the underlying block cipher is safe for concurrent use.
|
|
type xts struct {
|
|
b _cipher.Block
|
|
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
|
|
// only defined for 16-byte ciphers.
|
|
const blockSize = 16
|
|
|
|
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
|
|
}
|
|
|
|
// 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.
|
|
func NewGBXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) {
|
|
return newXTSEncrypter(cipherFunc, key, tweakKey, tweak, true)
|
|
}
|
|
|
|
// 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) {
|
|
tweak := make([]byte, blockSize)
|
|
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
|
|
return NewGBXTSEncrypter(cipherFunc, key, tweakKey, tweak)
|
|
}
|
|
|
|
func newXTSEncrypter(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.(xtsEncAble); ok {
|
|
var encryptedTweak [blockSize]byte
|
|
k2.Encrypt(encryptedTweak[:], tweak)
|
|
return xtsable.NewXTSEncrypter(&encryptedTweak, isGB), nil
|
|
}
|
|
|
|
c := &xts{
|
|
b: k1,
|
|
isGB: isGB,
|
|
}
|
|
k2.Encrypt(c.tweak[:], tweak)
|
|
return (*xtsEncrypter)(c), nil
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// CryptBlocks encrypts a sector of plaintext and puts the result into ciphertext.
|
|
// 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 *xtsEncrypter) CryptBlocks(ciphertext, plaintext []byte) {
|
|
if len(ciphertext) < len(plaintext) {
|
|
panic("xts: ciphertext is smaller than plaintext")
|
|
}
|
|
if len(plaintext) < blockSize {
|
|
panic("xts: plaintext length is smaller than the block size")
|
|
}
|
|
if alias.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
|
|
panic("xts: invalid buffer overlap")
|
|
}
|
|
|
|
lastCiphertext := ciphertext
|
|
|
|
if concCipher, ok := c.b.(concurrentBlocks); ok {
|
|
batchSize := concCipher.Concurrency() * blockSize
|
|
var tweaks []byte = make([]byte, batchSize)
|
|
|
|
for len(plaintext) >= batchSize {
|
|
for i := 0; i < concCipher.Concurrency(); i++ {
|
|
copy(tweaks[blockSize*i:], c.tweak[:])
|
|
mul2(&c.tweak, c.isGB)
|
|
}
|
|
subtle.XORBytes(ciphertext, plaintext, tweaks)
|
|
concCipher.EncryptBlocks(ciphertext, ciphertext)
|
|
subtle.XORBytes(ciphertext, ciphertext, tweaks)
|
|
plaintext = plaintext[batchSize:]
|
|
lastCiphertext = ciphertext[batchSize-blockSize:]
|
|
ciphertext = ciphertext[batchSize:]
|
|
}
|
|
}
|
|
for len(plaintext) >= blockSize {
|
|
subtle.XORBytes(ciphertext, plaintext, c.tweak[:])
|
|
c.b.Encrypt(ciphertext, ciphertext)
|
|
subtle.XORBytes(ciphertext, ciphertext, c.tweak[:])
|
|
plaintext = plaintext[blockSize:]
|
|
lastCiphertext = ciphertext
|
|
ciphertext = ciphertext[blockSize:]
|
|
mul2(&c.tweak, c.isGB)
|
|
}
|
|
// is there a final partial block to handle?
|
|
if remain := len(plaintext); remain > 0 {
|
|
var x [blockSize]byte
|
|
//Copy the final ciphertext bytes
|
|
copy(ciphertext, lastCiphertext[:remain])
|
|
//Copy the final plaintext bytes
|
|
copy(x[:], plaintext)
|
|
//Steal ciphertext to complete the block
|
|
copy(x[remain:], lastCiphertext[remain:blockSize])
|
|
//Merge the tweak into the input block
|
|
subtle.XORBytes(x[:], x[:], c.tweak[:])
|
|
//Encrypt the final block using K1
|
|
c.b.Encrypt(x[:], x[:])
|
|
//Merge the tweak into the output block
|
|
subtle.XORBytes(lastCiphertext, x[:], c.tweak[:])
|
|
}
|
|
}
|
|
|
|
func (c *xtsDecrypter) BlockSize() int {
|
|
return blockSize
|
|
}
|
|
|
|
// CryptBlocks 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 *xtsDecrypter) CryptBlocks(plaintext, ciphertext []byte) {
|
|
if len(plaintext) < len(ciphertext) {
|
|
panic("xts: plaintext is smaller than ciphertext")
|
|
}
|
|
if len(ciphertext) < blockSize {
|
|
panic("xts: ciphertext length is smaller than the block size")
|
|
}
|
|
if alias.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
|
|
panic("xts: invalid buffer overlap")
|
|
}
|
|
|
|
if concCipher, ok := c.b.(concurrentBlocks); ok {
|
|
batchSize := concCipher.Concurrency() * blockSize
|
|
var tweaks []byte = make([]byte, batchSize)
|
|
|
|
for len(ciphertext) >= batchSize {
|
|
for i := 0; i < concCipher.Concurrency(); i++ {
|
|
copy(tweaks[blockSize*i:], c.tweak[:])
|
|
mul2(&c.tweak, c.isGB)
|
|
}
|
|
subtle.XORBytes(plaintext, ciphertext, tweaks)
|
|
concCipher.DecryptBlocks(plaintext, plaintext)
|
|
subtle.XORBytes(plaintext, plaintext, tweaks)
|
|
plaintext = plaintext[batchSize:]
|
|
ciphertext = ciphertext[batchSize:]
|
|
}
|
|
}
|
|
|
|
for len(ciphertext) >= 2*blockSize {
|
|
subtle.XORBytes(plaintext, ciphertext, c.tweak[:])
|
|
c.b.Decrypt(plaintext, plaintext)
|
|
subtle.XORBytes(plaintext, plaintext, c.tweak[:])
|
|
plaintext = plaintext[blockSize:]
|
|
ciphertext = ciphertext[blockSize:]
|
|
|
|
mul2(&c.tweak, c.isGB)
|
|
}
|
|
|
|
if remain := len(ciphertext); remain >= blockSize {
|
|
var x [blockSize]byte
|
|
if remain > blockSize {
|
|
var tt [blockSize]byte
|
|
copy(tt[:], c.tweak[:])
|
|
mul2(&tt, c.isGB)
|
|
subtle.XORBytes(x[:], ciphertext, tt[:])
|
|
c.b.Decrypt(x[:], x[:])
|
|
subtle.XORBytes(plaintext, x[:], tt[:])
|
|
|
|
//Retrieve the length of the final block
|
|
remain -= blockSize
|
|
|
|
//Copy the final plaintext bytes
|
|
copy(plaintext[blockSize:], plaintext)
|
|
//Copy the final ciphertext bytes
|
|
copy(x[:], ciphertext[blockSize:])
|
|
//Steal ciphertext to complete the block
|
|
copy(x[remain:], plaintext[remain:blockSize])
|
|
} else {
|
|
//The last block contains exactly 128 bits
|
|
copy(x[:], ciphertext)
|
|
}
|
|
subtle.XORBytes(x[:], x[:], c.tweak[:])
|
|
c.b.Decrypt(x[:], x[:])
|
|
subtle.XORBytes(plaintext, x[:], c.tweak[:])
|
|
}
|
|
}
|
|
|
|
// mul2 multiplies tweak by 2 in GF(2¹²⁸) with an irreducible polynomial of
|
|
// x¹²⁸ + x⁷ + x² + x + 1.
|
|
func mul2(tweak *[blockSize]byte, isGB bool) {
|
|
var carryIn byte
|
|
if !isGB {
|
|
// tweak[0] represents the coefficients of {x^7, x^6, ..., x^0}
|
|
// tweak[15] represents the coefficients of {x^127, x^126, ..., x^120}
|
|
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 {
|
|
// GB/T 17964-2021, because of the bit-ordering, doubling is actually a right shift.
|
|
// tweak[0] represents the coefficients of {x^0, x^1, ..., x^7}
|
|
// tweak[15] represents the coefficients of {x^120, x^121, ..., x^127}
|
|
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
|
|
}
|
|
}
|
|
}
|