MAGIC - xts mode

This commit is contained in:
Emman 2021-04-08 11:12:52 +08:00
parent ddea2f74c8
commit b1184c24cf
5 changed files with 536 additions and 0 deletions

246
cipher/xts.go Normal file
View File

@ -0,0 +1,246 @@
package cipher
import (
_cipher "crypto/cipher"
"encoding/binary"
"errors"
"sync"
)
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)
}
// 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, 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.
//
Decrypt(dst, src []byte, sectorNum uint64)
}
// 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 {
k1, k2 _cipher.Block
}
// blockSize is the block size that the underlying cipher must have. XTS is
// only defined for 16-byte ciphers.
const blockSize = 16
var tweakPool = sync.Pool{
New: func() interface{} {
return new([blockSize]byte)
},
}
// NewXTS 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
// twice the length of the underlying cipher's key.
func NewXTS(cipherFunc CipherCreator, key []byte) (XTSBlockMode, error) {
k1, err := cipherFunc(key[:len(key)/2])
if err != nil {
return nil, err
}
k2, err := cipherFunc(key[len(key)/2:])
c := &xts{
k1,
k2,
}
if c.k1.BlockSize() != blockSize {
err = errors.New("xts: cipher does not have a block size of 16")
return nil, err
}
return c, nil
}
func (c *xts) BlockSize() int {
return blockSize
}
// Encrypt 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 *xts) Encrypt(ciphertext, plaintext []byte, sectorNum uint64) {
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 InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
panic("xts: invalid buffer overlap")
}
tweak := tweakPool.Get().(*[blockSize]byte)
for i := range tweak {
tweak[i] = 0
}
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
c.k2.Encrypt(tweak[:], tweak[:])
lastCiphertext := ciphertext
if concCipher, ok := c.k1.(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:], tweak[:])
mul2(tweak)
}
XorBytes(ciphertext, plaintext, tweaks)
concCipher.EncryptBlocks(ciphertext, ciphertext)
XorBytes(ciphertext, ciphertext, tweaks)
plaintext = plaintext[batchSize:]
lastCiphertext = ciphertext[batchSize-blockSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(plaintext) >= blockSize {
XorBytes(ciphertext, plaintext, tweak[:])
c.k1.Encrypt(ciphertext, ciphertext)
XorBytes(ciphertext, ciphertext, tweak[:])
plaintext = plaintext[blockSize:]
lastCiphertext = ciphertext
ciphertext = ciphertext[blockSize:]
mul2(tweak)
}
// 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
XorBytes(x[:], x[:], tweak[:])
//Encrypt the final block using K1
c.k1.Encrypt(x[:], x[:])
//Merge the tweak into the output block
XorBytes(lastCiphertext, x[:], tweak[:])
}
tweakPool.Put(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) Decrypt(plaintext, ciphertext []byte, sectorNum uint64) {
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 InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
panic("xts: invalid buffer overlap")
}
tweak := tweakPool.Get().(*[blockSize]byte)
for i := range tweak {
tweak[i] = 0
}
binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
c.k2.Encrypt(tweak[:], tweak[:])
if concCipher, ok := c.k1.(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:], tweak[:])
mul2(tweak)
}
XorBytes(plaintext, ciphertext, tweaks)
concCipher.DecryptBlocks(plaintext, plaintext)
XorBytes(plaintext, plaintext, tweaks)
plaintext = plaintext[batchSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(ciphertext) >= 2*blockSize {
XorBytes(plaintext, ciphertext, tweak[:])
c.k1.Decrypt(plaintext, plaintext)
XorBytes(plaintext, plaintext, tweak[:])
plaintext = plaintext[blockSize:]
ciphertext = ciphertext[blockSize:]
mul2(tweak)
}
if remain := len(ciphertext); remain >= blockSize {
var x [blockSize]byte
if remain > blockSize {
var tt [blockSize]byte
copy(tt[:], tweak[:])
mul2(&tt)
XorBytes(x[:], ciphertext, tt[:])
c.k1.Decrypt(x[:], x[:])
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)
}
XorBytes(x[:], x[:], tweak[:])
c.k1.Decrypt(x[:], x[:])
XorBytes(plaintext, x[:], tweak[:])
}
tweakPool.Put(tweak)
}
// mul2 multiplies tweak by 2 in GF(2¹²⁸) with an irreducible polynomial of
// x¹²⁸ + x⁷ + x² + x + 1.
func mul2(tweak *[blockSize]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] ^= GF128_FDBK // 1<<7 | 1<<2 | 1<<1 | 1
}
}

115
cipher/xts_test.go Normal file
View File

@ -0,0 +1,115 @@
package cipher
import (
"bytes"
"crypto/aes"
"encoding/hex"
"testing"
)
// These test vectors have been taken from IEEE P1619/D16, Annex B.
var xtsTestVectors = []struct {
key string
sector uint64
plaintext string
ciphertext string
}{
{ // XTS-AES-128 applied for a data unit of 32 bytes
"0000000000000000000000000000000000000000000000000000000000000000",
0,
"0000000000000000000000000000000000000000000000000000000000000000",
"917cf69ebd68b2ec9b9fe9a3eadda692cd43d2f59598ed858c02c2652fbf922e",
}, {
"1111111111111111111111111111111122222222222222222222222222222222",
0x3333333333,
"4444444444444444444444444444444444444444444444444444444444444444",
"c454185e6a16936e39334038acef838bfb186fff7480adc4289382ecd6d394f0",
}, {
"fffefdfcfbfaf9f8f7f6f5f4f3f2f1f022222222222222222222222222222222",
0x3333333333,
"4444444444444444444444444444444444444444444444444444444444444444",
"af85336b597afc1a900b2eb21ec949d292df4c047e0b21532186a5971a227a89",
}, { // XTS-AES-128 applied for a data unit of 512 bytes
"2718281828459045235360287471352631415926535897932384626433832795",
0,
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
"27a7479befa1d476489f308cd4cfa6e2a96e4bbe3208ff25287dd3819616e89cc78cf7f5e543445f8333d8fa7f56000005279fa5d8b5e4ad40e736ddb4d35412328063fd2aab53e5ea1e0a9f332500a5df9487d07a5c92cc512c8866c7e860ce93fdf166a24912b422976146ae20ce846bb7dc9ba94a767aaef20c0d61ad02655ea92dc4c4e41a8952c651d33174be51a10c421110e6d81588ede82103a252d8a750e8768defffed9122810aaeb99f9172af82b604dc4b8e51bcb08235a6f4341332e4ca60482a4ba1a03b3e65008fc5da76b70bf1690db4eae29c5f1badd03c5ccf2a55d705ddcd86d449511ceb7ec30bf12b1fa35b913f9f747a8afd1b130e94bff94effd01a91735ca1726acd0b197c4e5b03393697e126826fb6bbde8ecc1e08298516e2c9ed03ff3c1b7860f6de76d4cecd94c8119855ef5297ca67e9f3e7ff72b1e99785ca0a7e7720c5b36dc6d72cac9574c8cbbc2f801e23e56fd344b07f22154beba0f08ce8891e643ed995c94d9a69c9f1b5f499027a78572aeebd74d20cc39881c213ee770b1010e4bea718846977ae119f7a023ab58cca0ad752afe656bb3c17256a9f6e9bf19fdd5a38fc82bbe872c5539edb609ef4f79c203ebb140f2e583cb2ad15b4aa5b655016a8449277dbd477ef2c8d6c017db738b18deb4a427d1923ce3ff262735779a418f20a282df920147beabe421ee5319d0568",
}, { // Vector 5
"2718281828459045235360287471352631415926535897932384626433832795",
1,
"27a7479befa1d476489f308cd4cfa6e2a96e4bbe3208ff25287dd3819616e89cc78cf7f5e543445f8333d8fa7f56000005279fa5d8b5e4ad40e736ddb4d35412328063fd2aab53e5ea1e0a9f332500a5df9487d07a5c92cc512c8866c7e860ce93fdf166a24912b422976146ae20ce846bb7dc9ba94a767aaef20c0d61ad02655ea92dc4c4e41a8952c651d33174be51a10c421110e6d81588ede82103a252d8a750e8768defffed9122810aaeb99f9172af82b604dc4b8e51bcb08235a6f4341332e4ca60482a4ba1a03b3e65008fc5da76b70bf1690db4eae29c5f1badd03c5ccf2a55d705ddcd86d449511ceb7ec30bf12b1fa35b913f9f747a8afd1b130e94bff94effd01a91735ca1726acd0b197c4e5b03393697e126826fb6bbde8ecc1e08298516e2c9ed03ff3c1b7860f6de76d4cecd94c8119855ef5297ca67e9f3e7ff72b1e99785ca0a7e7720c5b36dc6d72cac9574c8cbbc2f801e23e56fd344b07f22154beba0f08ce8891e643ed995c94d9a69c9f1b5f499027a78572aeebd74d20cc39881c213ee770b1010e4bea718846977ae119f7a023ab58cca0ad752afe656bb3c17256a9f6e9bf19fdd5a38fc82bbe872c5539edb609ef4f79c203ebb140f2e583cb2ad15b4aa5b655016a8449277dbd477ef2c8d6c017db738b18deb4a427d1923ce3ff262735779a418f20a282df920147beabe421ee5319d0568",
"264d3ca8512194fec312c8c9891f279fefdd608d0c027b60483a3fa811d65ee59d52d9e40ec5672d81532b38b6b089ce951f0f9c35590b8b978d175213f329bb1c2fd30f2f7f30492a61a532a79f51d36f5e31a7c9a12c286082ff7d2394d18f783e1a8e72c722caaaa52d8f065657d2631fd25bfd8e5baad6e527d763517501c68c5edc3cdd55435c532d7125c8614deed9adaa3acade5888b87bef641c4c994c8091b5bcd387f3963fb5bc37aa922fbfe3df4e5b915e6eb514717bdd2a74079a5073f5c4bfd46adf7d282e7a393a52579d11a028da4d9cd9c77124f9648ee383b1ac763930e7162a8d37f350b2f74b8472cf09902063c6b32e8c2d9290cefbd7346d1c779a0df50edcde4531da07b099c638e83a755944df2aef1aa31752fd323dcb710fb4bfbb9d22b925bc3577e1b8949e729a90bbafeacf7f7879e7b1147e28ba0bae940db795a61b15ecf4df8db07b824bb062802cc98a9545bb2aaeed77cb3fc6db15dcd7d80d7d5bc406c4970a3478ada8899b329198eb61c193fb6275aa8ca340344a75a862aebe92eee1ce032fd950b47d7704a3876923b4ad62844bf4a09c4dbe8b4397184b7471360c9564880aedddb9baa4af2e75394b08cd32ff479c57a07d3eab5d54de5f9738b8d27f27a9f0ab11799d7b7ffefb2704c95c6ad12c39f1e867a4b7b1d7818a4b753dfd2a89ccb45e001a03a867b187f225dd",
}, { // Vector 10, XTS-AES-256 applied for a data unit of 512 bytes
"27182818284590452353602874713526624977572470936999595749669676273141592653589793238462643383279502884197169399375105820974944592",
0xff,
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
"1c3b3a102f770386e4836c99e370cf9bea00803f5e482357a4ae12d414a3e63b5d31e276f8fe4a8d66b317f9ac683f44680a86ac35adfc3345befecb4bb188fd5776926c49a3095eb108fd1098baec70aaa66999a72a82f27d848b21d4a741b0c5cd4d5fff9dac89aeba122961d03a757123e9870f8acf1000020887891429ca2a3e7a7d7df7b10355165c8b9a6d0a7de8b062c4500dc4cd120c0f7418dae3d0b5781c34803fa75421c790dfe1de1834f280d7667b327f6c8cd7557e12ac3a0f93ec05c52e0493ef31a12d3d9260f79a289d6a379bc70c50841473d1a8cc81ec583e9645e07b8d9670655ba5bbcfecc6dc3966380ad8fecb17b6ba02469a020a84e18e8f84252070c13e9f1f289be54fbc481457778f616015e1327a02b140f1505eb309326d68378f8374595c849d84f4c333ec4423885143cb47bd71c5edae9be69a2ffeceb1bec9de244fbe15992b11b77c040f12bd8f6a975a44a0f90c29a9abc3d4d893927284c58754cce294529f8614dcd2aba991925fedc4ae74ffac6e333b93eb4aff0479da9a410e4450e0dd7ae4c6e2910900575da401fc07059f645e8b7e9bfdef33943054ff84011493c27b3429eaedb4ed5376441a77ed43851ad77f16f541dfd269d50d6a5f14fb0aab1cbb4c1550be97f7ab4066193c4caa773dad38014bd2092fa755c824bb5e54c4f36ffda9fcea70b9c6e693e148c151",
}, { // XTS-AES-128 applied for a data unit that is not a multiple of 16 bytes, but should be a complte byte
"c46acc2e7e013cb71cdbf750cf76b000249fbf4fb6cd17607773c23ffa2c4330",
94,
"7e9c2289cba460e470222953439cdaa892a5433d4dab2a3f67",
"9af624641d42b036377ef37b4a158f49e49f6ee308ad449ecf",
}, {
"56ffcc9bbbdf413f0fc0f888f44b7493bb1925a39b8adf02d9009bb16db0a887",
144,
"9a839cc14363bafcfc0cc93b14f8e769d35b94cc98267438e3",
"fbd8dcfc4d662259f48b151728c3b37233a35127a77051ee9d",
},
{
"7454a43b87b1cf0dec95032c22873be3cace3bb795568854c1a008c07c5813f3",
108,
"41088fa15195b2733fe824d2c1fdc8306080863945fb2a73cf",
"f916d877f817ae390f42dc54723bda0ad3ba5f331a72d05ccb",
},
}
func fromHex(s string) []byte {
ret, err := hex.DecodeString(s)
if err != nil {
panic("xts: invalid hex in test")
}
return ret
}
func TestXTS(t *testing.T) {
for i, test := range xtsTestVectors {
c, err := NewXTS(aes.NewCipher, fromHex(test.key))
if err != nil {
t.Errorf("#%d: failed to create cipher: %s", i, err)
continue
}
plaintext := fromHex(test.plaintext)
ciphertext := make([]byte, len(plaintext))
c.Encrypt(ciphertext, plaintext, test.sector)
expectedCiphertext := fromHex(test.ciphertext)
if !bytes.Equal(ciphertext, expectedCiphertext) {
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
continue
}
decrypted := make([]byte, len(ciphertext))
c.Decrypt(decrypted, ciphertext, test.sector)
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
}
}
}
func TestShorterCiphertext(t *testing.T) {
c, err := NewXTS(aes.NewCipher, make([]byte, 32))
if err != nil {
t.Fatalf("NewCipher failed: %s", err)
}
plaintext := make([]byte, 32)
encrypted := make([]byte, 48)
decrypted := make([]byte, 48)
c.Encrypt(encrypted, plaintext, 0)
c.Decrypt(decrypted, encrypted[:len(plaintext)], 0)
if !bytes.Equal(plaintext, decrypted[:len(plaintext)]) {
t.Errorf("En/Decryption is not inverse")
}
}

View File

@ -39,8 +39,12 @@ func newCipher(key []byte) (cipher.Block, error) {
const FourBlocksSize = 64 const FourBlocksSize = 64
const BatchBlocks = 4
func (c *sm4CipherAsm) BlockSize() int { return BlockSize } func (c *sm4CipherAsm) BlockSize() int { return BlockSize }
func (c *sm4CipherAsm) Concurrency() int { return BatchBlocks }
func (c *sm4CipherAsm) Encrypt(dst, src []byte) { func (c *sm4CipherAsm) Encrypt(dst, src []byte) {
if len(src) < BlockSize { if len(src) < BlockSize {
panic("sm4: input not full block") panic("sm4: input not full block")
@ -54,6 +58,19 @@ func (c *sm4CipherAsm) Encrypt(dst, src []byte) {
encryptBlockAsm(&c.enc[0], &dst[0], &src[0]) encryptBlockAsm(&c.enc[0], &dst[0], &src[0])
} }
func (c *sm4CipherAsm) EncryptBlocks(dst, src []byte) {
if len(src) < FourBlocksSize {
panic("sm4: input not full blocks")
}
if len(dst) < FourBlocksSize {
panic("sm4: output not full blocks")
}
if smcipher.InexactOverlap(dst[:FourBlocksSize], src[:FourBlocksSize]) {
panic("sm4: invalid buffer overlap")
}
encryptBlocksAsm(&c.enc[0], &dst[0], &src[0])
}
func (c *sm4CipherAsm) Decrypt(dst, src []byte) { func (c *sm4CipherAsm) Decrypt(dst, src []byte) {
if len(src) < BlockSize { if len(src) < BlockSize {
panic("sm4: input not full block") panic("sm4: input not full block")
@ -67,6 +84,19 @@ func (c *sm4CipherAsm) Decrypt(dst, src []byte) {
encryptBlockAsm(&c.dec[0], &dst[0], &src[0]) encryptBlockAsm(&c.dec[0], &dst[0], &src[0])
} }
func (c *sm4CipherAsm) DecryptBlocks(dst, src []byte) {
if len(src) < FourBlocksSize {
panic("sm4: input not full blocks")
}
if len(dst) < FourBlocksSize {
panic("sm4: output not full blocks")
}
if smcipher.InexactOverlap(dst[:FourBlocksSize], src[:FourBlocksSize]) {
panic("sm4: invalid buffer overlap")
}
encryptBlocksAsm(&c.dec[0], &dst[0], &src[0])
}
// expandKey is used by BenchmarkExpand to ensure that the asm implementation // expandKey is used by BenchmarkExpand to ensure that the asm implementation
// of key expansion is used for the benchmark when it is available. // of key expansion is used for the benchmark when it is available.
func expandKey(key []byte, enc, dec []uint32) { func expandKey(key []byte, enc, dec []uint32) {

View File

@ -361,3 +361,55 @@ func benchmarkSM4CCMOpen(b *testing.B, buf []byte) {
sm4gcm, _ := smcipher.NewCCM(c) sm4gcm, _ := smcipher.NewCCM(c)
benchmarkGCMOpen(b, sm4gcm, buf) benchmarkGCMOpen(b, sm4gcm, buf)
} }
func benchmarkXTS(b *testing.B, cipherFunc func([]byte) (cipher.Block, error), length, keylen int64) {
c, err := smcipher.NewXTS(cipherFunc, make([]byte, keylen))
if err != nil {
b.Fatalf("NewCipher failed: %s", err)
}
plaintext := make([]byte, length)
encrypted := make([]byte, length)
//decrypted := make([]byte, length)
b.SetBytes(int64(len(plaintext)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
c.Encrypt(encrypted, plaintext, 0)
//c.Decrypt(decrypted, encrypted[:len(plaintext)], 0)
}
}
func BenchmarkAES128XTSEncrypt512(b *testing.B) {
benchmarkXTS(b, aes.NewCipher, 512, 32)
}
func BenchmarkAES128XTSEncrypt1K(b *testing.B) {
benchmarkXTS(b, aes.NewCipher, 1024, 32)
}
func BenchmarkAES128XTSEncrypt4K(b *testing.B) {
benchmarkXTS(b, aes.NewCipher, 4096, 32)
}
func BenchmarkAES256XTSEncrypt512(b *testing.B) {
benchmarkXTS(b, aes.NewCipher, 512, 64)
}
func BenchmarkAES256XTSEncrypt1K(b *testing.B) {
benchmarkXTS(b, aes.NewCipher, 1024, 64)
}
func BenchmarkAES256XTSEncrypt4K(b *testing.B) {
benchmarkXTS(b, aes.NewCipher, 4096, 64)
}
func BenchmarkSM4XTSEncrypt512(b *testing.B) {
benchmarkXTS(b, sm4.NewCipher, 512, 32)
}
func BenchmarkSM4XTSEncrypt1K(b *testing.B) {
benchmarkXTS(b, sm4.NewCipher, 1024, 32)
}
func BenchmarkSM4XTSEncrypt4K(b *testing.B) {
benchmarkXTS(b, sm4.NewCipher, 4096, 32)
}

93
sm4_test/xts_sm4_test.go Normal file
View File

@ -0,0 +1,93 @@
package sm4_test
import (
"bytes"
"encoding/hex"
"testing"
"github.com/emmansun/gmsm/cipher"
"github.com/emmansun/gmsm/sm4"
)
var xtsTestVectors = []struct {
key string
sector uint64
plaintext string
ciphertext string
}{
{ // XTS-SM4-128 applied for a data unit of 32 bytes
"0000000000000000000000000000000000000000000000000000000000000000",
0,
"0000000000000000000000000000000000000000000000000000000000000000",
"d9b421f731c894fdc35b77291fe4e3b02a1fb76698d59f0e51376c4ada5bc75d",
}, {
"1111111111111111111111111111111122222222222222222222222222222222",
0x3333333333,
"4444444444444444444444444444444444444444444444444444444444444444",
"a74d726c11196a32be04e001ff29d0c7932f9f3ec29bfcb64dd17f63cbd3ea31",
}, {
"fffefdfcfbfaf9f8f7f6f5f4f3f2f1f022222222222222222222222222222222",
0x3333333333,
"4444444444444444444444444444444444444444444444444444444444444444",
"7f76088effadf70c02ea9f95da0628d351bfcb9eac0563bcf17b710dab0a9826",
}, { // XTS-SM4-128 applied for a data unit of 512 bytes
"2718281828459045235360287471352631415926535897932384626433832795",
0,
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
"54dd65b6326faea8fad1a83c63614af39f721d8dfe177a30b66abf6a449980e1cdbe06afb73336f37a4d39de964a30d7d04a3799169c60258f6b748a61861aa5ec92a2c15b2b7c615a42aba499bbd6b71db9c789b2182089a25dd3df800ed1864d19f7ed45fd17a9480b0fb82d9b7fc3ed57e9a1140eaa778dd2dd679e3edc3dc4d55c950ebc531d9592f7c4638256d56518292a20af98fdd3a63600350a70ab5a40f4c285037ca01f251f19ecae0329ff77ad88cd5a4cdea2aeabc22148ffbd239bd10515bde1131dec8404e443dc763140d5f22bf33e0c6872d6b81d630f6f00cdd058fe80f9cbfb77707f93cee2ca92b915b8304027c190a84e2d65e018cc6a387d3766acdb28253284e8db9acf8f52280ddc6d0033d2ccaaa4f9aeff123669bc024fd6768edf8bc1f8d622c19c609ef97f609190cd110241e7fb084ed8942da1f9b9cf1b514b61a388b30ea61a4a745b381ee7ad6c4db1275453b8413f98df6e4a40986ee4b59af5dfaecd301265179067a00d7ca35ab95abd617adea28ec1c26a97de28b8bfe30120d6aefbd258c59e42d161e8065a78106bdca5cd90fb3aac4e93866c8a7f9676860a79145bd92e02e819a90be0b97cc522b32106856fdf0e54d88e4624155a2f1c14eaeaa163f858e99a806e791acd82f1b0e29f0028a4c38e976f571a93f4fd57d787c24db0e01ca304e5a5c4dd50cf8bdbf491e57c",
}, { // Vector 5
"2718281828459045235360287471352631415926535897932384626433832795",
1,
"27a7479befa1d476489f308cd4cfa6e2a96e4bbe3208ff25287dd3819616e89cc78cf7f5e543445f8333d8fa7f56000005279fa5d8b5e4ad40e736ddb4d35412328063fd2aab53e5ea1e0a9f332500a5df9487d07a5c92cc512c8866c7e860ce93fdf166a24912b422976146ae20ce846bb7dc9ba94a767aaef20c0d61ad02655ea92dc4c4e41a8952c651d33174be51a10c421110e6d81588ede82103a252d8a750e8768defffed9122810aaeb99f9172af82b604dc4b8e51bcb08235a6f4341332e4ca60482a4ba1a03b3e65008fc5da76b70bf1690db4eae29c5f1badd03c5ccf2a55d705ddcd86d449511ceb7ec30bf12b1fa35b913f9f747a8afd1b130e94bff94effd01a91735ca1726acd0b197c4e5b03393697e126826fb6bbde8ecc1e08298516e2c9ed03ff3c1b7860f6de76d4cecd94c8119855ef5297ca67e9f3e7ff72b1e99785ca0a7e7720c5b36dc6d72cac9574c8cbbc2f801e23e56fd344b07f22154beba0f08ce8891e643ed995c94d9a69c9f1b5f499027a78572aeebd74d20cc39881c213ee770b1010e4bea718846977ae119f7a023ab58cca0ad752afe656bb3c17256a9f6e9bf19fdd5a38fc82bbe872c5539edb609ef4f79c203ebb140f2e583cb2ad15b4aa5b655016a8449277dbd477ef2c8d6c017db738b18deb4a427d1923ce3ff262735779a418f20a282df920147beabe421ee5319d0568",
"0e36ba273dd121afc77e0d8c00aa4a662b21f363470d333f2fe2ddcbcc51ecd523022f5fa7970062800cd3859cacead369263681543db431f3844a3638e837cf025cecc3b778e14ac1fd02bb684d0e3cc3d05758cf4b3827bae92f9f09a45487e0a830154a4206a14c4077bcc928e6039b78cdf8f915236c5a4efc21a0ba7173232cef6f18f8b53be5e1eb37282bed31a24f322cf1bba02dfd2583ce216a73726915116fd8ce46d58aa562b5a5d88076792d6e35cba40552db6a19776eaf255c3fc927adc41cb83a83884f98176267f37e543ce34fa32960d1d05aa05ff04103037a730175f1d59a32b64f308925fc9fa9c60421b4ab438e14504227cba20c8c06b508554fb02e52b92a1cd0a8e386511bc4c2fb62998d0ac5d9e7614080a10039b8cddf24a644b3e0aa02bb5d6c0897a84bfe0d12690cbd9fb92fd39b5b9504deeeaab0c5b9839b6283b87abe6439d28f0afb0508104fd4db9fd6e0301c6a488e76fd2a4801d2b7df57e0179506e9a8dbd7312be3922ea4e7339227061485452296dabc3b0f178a2e4ba012bbb6e836dec5d25abaa0f399ca622c5f075dfae7b2ffef4e396cd74b9bc3aeb7c212a5fd5c42b73fcf92e1f4ca458bb50e7257c4ffea253f30f7eaf9a6762ce15177f55ba250a4293d6ecdbd2e9a80c942b38dbdbd74773245a7a7db6b91d1f6c74bd32b7a7a193a2d260d266b64dd19b959ae42",
}, { // XTS-SM4-128 applied for a data unit that is not a multiple of 16 bytes, but should be a complte byte
"c46acc2e7e013cb71cdbf750cf76b000249fbf4fb6cd17607773c23ffa2c4330",
94,
"7e9c2289cba460e470222953439cdaa892a5433d4dab2a3f67",
"c3cf5445c64aa518f4abce2848faddfb4605d9fb66f1f12c0c",
}, {
"56ffcc9bbbdf413f0fc0f888f44b7493bb1925a39b8adf02d9009bb16db0a887",
144,
"9a839cc14363bafcfc0cc93b14f8e769d35b94cc98267438e3",
"af027012c829206c32a31706999d046f10a83bcacbc5c96353",
},
{
"7454a43b87b1cf0dec95032c22873be3cace3bb795568854c1a008c07c5813f3",
108,
"41088fa15195b2733fe824d2c1fdc8306080863945fb2a73cf",
"614ee9311a53791889338eb2f66fedff7dc15126349bed1465",
},
}
func fromHex(s string) []byte {
ret, err := hex.DecodeString(s)
if err != nil {
panic("xts: invalid hex in test")
}
return ret
}
func TestXTS(t *testing.T) {
for i, test := range xtsTestVectors {
c, err := cipher.NewXTS(sm4.NewCipher, fromHex(test.key))
if err != nil {
t.Errorf("#%d: failed to create cipher: %s", i, err)
continue
}
plaintext := fromHex(test.plaintext)
ciphertext := make([]byte, len(plaintext))
c.Encrypt(ciphertext, plaintext, test.sector)
expectedCiphertext := fromHex(test.ciphertext)
if !bytes.Equal(ciphertext, expectedCiphertext) {
t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
continue
}
decrypted := make([]byte, len(ciphertext))
c.Decrypt(decrypted, ciphertext, test.sector)
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
}
}
}