mirror of
https://github.com/xtaci/kcp-go.git
synced 2026-10-20 00:15:39 +00:00
462 lines
9.2 KiB
Go
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")
|
|
}
|
|
}
|