Files
kcp-go/crypt_test.go
xtaci 052b09682f refine tests for better coverage
mv entropy related test to entropy_test.go
2025-12-19 21:59:05 +08:00

462 lines
9.2 KiB
Go

// The MIT License (MIT)
//
// Copyright (c) 2015 xtaci
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package kcp
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/binary"
"hash/crc32"
"io"
"testing"
"golang.org/x/crypto/chacha20poly1305"
)
func TestSM4(t *testing.T) {
bc, err := NewSM4BlockCrypt(pass[:16])
if err != nil {
t.Fatal(err)
return
}
cryptTest(t, bc)
}
func TestAES(t *testing.T) {
bc, err := NewAESBlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
return
}
cryptTest(t, bc)
}
func TestTEA(t *testing.T) {
bc, err := NewTEABlockCrypt(pass[:16])
if err != nil {
t.Fatal(err)
return
}
cryptTest(t, bc)
}
func TestXOR(t *testing.T) {
bc, err := NewSimpleXORBlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
return
}
cryptTest(t, bc)
}
func TestBlowfish(t *testing.T) {
bc, err := NewBlowfishBlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
return
}
cryptTest(t, bc)
}
func TestNone(t *testing.T) {
bc, err := NewNoneBlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
return
}
cryptTest(t, bc)
}
func TestCast5(t *testing.T) {
bc, err := NewCast5BlockCrypt(pass[:16])
if err != nil {
t.Fatal(err)
return
}
cryptTest(t, bc)
}
func Test3DES(t *testing.T) {
bc, err := NewTripleDESBlockCrypt(pass[:24])
if err != nil {
t.Fatal(err)
return
}
cryptTest(t, bc)
}
func TestTwofish(t *testing.T) {
bc, err := NewTwofishBlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
return
}
cryptTest(t, bc)
}
func TestXTEA(t *testing.T) {
bc, err := NewXTEABlockCrypt(pass[:16])
if err != nil {
t.Fatal(err)
return
}
cryptTest(t, bc)
}
func TestSalsa20(t *testing.T) {
bc, err := NewSalsa20BlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
return
}
cryptTest(t, bc)
}
func cryptTest(t *testing.T, bc BlockCrypt) {
data := make([]byte, mtuLimit)
io.ReadFull(rand.Reader, data)
dec := make([]byte, mtuLimit)
enc := make([]byte, mtuLimit)
bc.Encrypt(enc, data)
bc.Decrypt(dec, enc)
if !bytes.Equal(data, dec) {
t.Fail()
}
}
func TestAES256GCM(t *testing.T) {
bc, err := NewAESGCMCrypt(pass[:32])
if err != nil {
t.Fatal(err)
return
}
testAEAD(t, bc)
}
func TestAES128GCM(t *testing.T) {
bc, err := NewAESGCMCrypt(pass[:16])
if err != nil {
t.Fatal(err)
return
}
testAEAD(t, bc)
}
func testAEAD(t *testing.T, bc BlockCrypt) {
aead := bc.(*aeadCrypt)
nonceSize := aead.NonceSize()
size := mtuLimit - cryptHeaderSize - aead.Overhead()
data := make([]byte, size)
io.ReadFull(rand.Reader, data)
// if the size of packet is cannot accommodate the AEAD overhead
// Open and Seal will allocate a new slice internally, we need to
// ensure that it does not happen for our MTU sized packets.
packet := make([]byte, mtuLimit)
// Seal
dst := packet[:nonceSize]
nonce := packet[:nonceSize]
fillRand(nonce)
sealedPacket := aead.Seal(dst, nonce, data, nil)
if &sealedPacket[0] != &packet[0] {
t.Fatal("Seal created a new slice")
return
}
// Open
dst = sealedPacket[:nonceSize]
nonce = sealedPacket[:nonceSize]
ciphertext := sealedPacket[nonceSize:]
decrypted, err := aead.Open(dst, nonce, ciphertext, nil)
if &decrypted[0] != &sealedPacket[0] {
t.Fatal("Open created a new slice")
return
}
if err != nil {
t.Fatal(err)
return
}
if !bytes.Equal(data, decrypted[nonceSize:]) {
t.Fail()
}
}
func BenchmarkSM4(b *testing.B) {
bc, err := NewSM4BlockCrypt(pass[:16])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func BenchmarkAES128(b *testing.B) {
bc, err := NewAESBlockCrypt(pass[:16])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func BenchmarkAES192(b *testing.B) {
bc, err := NewAESBlockCrypt(pass[:24])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func BenchmarkAES256(b *testing.B) {
bc, err := NewAESBlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func BenchmarkTEA(b *testing.B) {
bc, err := NewTEABlockCrypt(pass[:16])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func BenchmarkXOR(b *testing.B) {
bc, err := NewSimpleXORBlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func BenchmarkBlowfish(b *testing.B) {
bc, err := NewBlowfishBlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func BenchmarkNone(b *testing.B) {
bc, err := NewNoneBlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func BenchmarkCast5(b *testing.B) {
bc, err := NewCast5BlockCrypt(pass[:16])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func Benchmark3DES(b *testing.B) {
bc, err := NewTripleDESBlockCrypt(pass[:24])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func BenchmarkTwofish(b *testing.B) {
bc, err := NewTwofishBlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkXTEA(b *testing.B) {
bc, err := NewXTEABlockCrypt(pass[:16])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func BenchmarkSalsa20(b *testing.B) {
bc, err := NewSalsa20BlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
return
}
benchCrypt(b, bc)
}
func benchCrypt(b *testing.B, bc BlockCrypt) {
data := make([]byte, mtuLimit)
io.ReadFull(rand.Reader, data)
dec := make([]byte, mtuLimit)
enc := make([]byte, mtuLimit)
b.ReportAllocs()
b.SetBytes(int64(len(enc) * 2))
for b.Loop() {
bc.Encrypt(enc, data)
bc.Decrypt(dec, enc)
}
}
func BenchmarkCRC32(b *testing.B) {
content := make([]byte, 1024)
b.SetBytes(int64(len(content)))
for b.Loop() {
crc32.ChecksumIEEE(content)
}
}
func BenchmarkCFB_AES_128_CRC32(b *testing.B) {
bc, err := NewAESBlockCrypt(pass[:16])
if err != nil {
b.Fatal(err)
return
}
data := make([]byte, 1400, mtuLimit)
b.SetBytes(1400)
for b.Loop() {
checksum := crc32.ChecksumIEEE(data[cryptHeaderSize:])
binary.LittleEndian.PutUint32(data[nonceSize:cryptHeaderSize], checksum)
bc.Encrypt(data, data)
}
}
func BenchmarkAEAD_AES_128_GCM(b *testing.B) {
block, err := aes.NewCipher(pass[:16])
if err != nil {
panic(err)
}
aead, err := cipher.NewGCM(block)
if err != nil {
panic(err)
}
data := make([]byte, 1400, mtuLimit)
b.SetBytes(1400)
nonce := data[:aead.NonceSize()]
plaintext := data[aead.NonceSize():]
for b.Loop() {
aead.Seal(plaintext[:0], nonce, plaintext, nil)
}
}
func BenchmarkCFB_Salsa20_CRC32(b *testing.B) {
bc, err := NewSalsa20BlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
return
}
data := make([]byte, 1400, mtuLimit)
b.SetBytes(1400)
for b.Loop() {
checksum := crc32.ChecksumIEEE(data[cryptHeaderSize:])
binary.LittleEndian.PutUint32(data[nonceSize:cryptHeaderSize], checksum)
bc.Encrypt(data, data)
}
}
func BenchmarkAEAD_Chacha20_Poly1035(b *testing.B) {
aead, err := chacha20poly1305.New(pass[:32])
if err != nil {
panic(err)
}
data := make([]byte, 1400, mtuLimit)
b.SetBytes(1400)
nonce := data[:aead.NonceSize()]
plaintext := data[aead.NonceSize():]
for b.Loop() {
aead.Seal(plaintext[:0], nonce, plaintext, nil)
}
}
func TestCryptErrors(t *testing.T) {
invalidKey := []byte("invalid")
if _, err := NewSM4BlockCrypt(invalidKey); err == nil {
t.Error("NewSM4BlockCrypt should fail with invalid key")
}
if _, err := NewTwofishBlockCrypt(invalidKey); err == nil {
t.Error("NewTwofishBlockCrypt should fail with invalid key")
}
if _, err := NewTripleDESBlockCrypt(invalidKey); err == nil {
t.Error("NewTripleDESBlockCrypt should fail with invalid key")
}
if _, err := NewCast5BlockCrypt(invalidKey); err == nil {
t.Error("NewCast5BlockCrypt should fail with invalid key")
}
// Blowfish supports variable key length, so "invalid" (7 bytes) might be valid.
// Blowfish key size: 1-56 bytes. So 7 bytes is valid.
// Let's try empty key or very long key.
if _, err := NewBlowfishBlockCrypt(nil); err == nil {
t.Error("NewBlowfishBlockCrypt should fail with nil key")
}
if _, err := NewAESBlockCrypt(invalidKey); err == nil {
t.Error("NewAESBlockCrypt should fail with invalid key")
}
if _, err := NewTEABlockCrypt(invalidKey); err == nil {
t.Error("NewTEABlockCrypt should fail with invalid key")
}
if _, err := NewXTEABlockCrypt(invalidKey); err == nil {
t.Error("NewXTEABlockCrypt should fail with invalid key")
}
}