From 692ff9a488e35119d4d3ddb2e0260891b161ab59 Mon Sep 17 00:00:00 2001 From: FishGoddess <1149062639@qq.com> Date: Wed, 22 May 2024 01:08:28 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=20des=20=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- aes/aes_test.go | 2 +- des/cbc.go | 63 ----------- des/cbc_test.go | 97 ---------------- des/cfb.go | 63 ----------- des/cfb_test.go | 97 ---------------- des/ctr.go | 63 ----------- des/ctr_test.go | 97 ---------------- des/des.go | 175 +++++++++++++++++++++++++---- des/des_test.go | 238 +++++++++++++++++++++++++++++++++++++-- des/ecb.go | 94 ---------------- des/ecb_test.go | 97 ---------------- des/ofb.go | 63 ----------- des/ofb_test.go | 97 ---------------- des/triple_des.go | 179 ++++++++++++++++++++++++++++++ des/triple_des_test.go | 246 +++++++++++++++++++++++++++++++++++++++++ 16 files changed, 809 insertions(+), 864 deletions(-) delete mode 100644 des/cbc.go delete mode 100644 des/cbc_test.go delete mode 100644 des/cfb.go delete mode 100644 des/cfb_test.go delete mode 100644 des/ctr.go delete mode 100644 des/ctr_test.go delete mode 100644 des/ecb.go delete mode 100644 des/ecb_test.go delete mode 100644 des/ofb.go delete mode 100644 des/ofb_test.go create mode 100644 des/triple_des.go create mode 100644 des/triple_des_test.go diff --git a/Makefile b/Makefile index 2d001f0..2692b56 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ fmt: go fmt ./... test: - go test -v -cover -count=1 -test.cpu=1 ./... + go test -cover -count=1 -test.cpu=1 ./... bench: go test -v ./_examples/hash_test.go -bench=. -benchtime=1s diff --git a/aes/aes_test.go b/aes/aes_test.go index 085ed8b..7724565 100644 --- a/aes/aes_test.go +++ b/aes/aes_test.go @@ -64,7 +64,7 @@ func TestNewBlock(t *testing.T) { } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestAESECB$ +// go test -v -cover -count=1 -test.cpu=1 -run=^TestECB$ func TestECB(t *testing.T) { cases := map[string]*testResult{ "": { diff --git a/des/cbc.go b/des/cbc.go deleted file mode 100644 index c6b26a4..0000000 --- a/des/cbc.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package des - -import ( - "crypto/cipher" - - "github.com/FishGoddess/cryptox" -) - -func encryptCBC(block cipher.Block, padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - src := padding(bs.Clone(), block.BlockSize()) - dst := src.Clone() - - cipher.NewCBCEncrypter(block, iv).CryptBlocks(dst, src) - return dst, nil -} - -func decryptCBC(block cipher.Block, unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - src := bs - dst := bs.Clone() - - cipher.NewCBCDecrypter(block, iv).CryptBlocks(dst, src) - return unpadding(dst, block.BlockSize()) -} - -func (d DES) EncryptCBC(padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := d.block, d.err - if err != nil { - return nil, err - } - - return encryptCBC(block, padding, iv, bs) -} - -func (d DES) DecryptCBC(unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := d.block, d.err - if err != nil { - return nil, err - } - - return decryptCBC(block, unpadding, iv, bs) -} - -func (td TripleDES) EncryptCBC(padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := td.block, td.err - if err != nil { - return nil, err - } - - return encryptCBC(block, padding, iv, bs) -} - -func (td TripleDES) DecryptCBC(unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := td.block, td.err - if err != nil { - return nil, err - } - - return decryptCBC(block, unpadding, iv, bs) -} diff --git a/des/cbc_test.go b/des/cbc_test.go deleted file mode 100644 index 03ce4e6..0000000 --- a/des/cbc_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package des - -import ( - "testing" - - "github.com/FishGoddess/cryptox" -) - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestDESCBC$ -func TestDESCBC(t *testing.T) { - des := New(testKey) - - cases := map[string]*testResult{ - "": { - bs: []byte{205, 172, 198, 131, 218, 176, 175, 188}, - hexString: "cdacc683dab0afbc", - base64String: "zazGg9qwr7w=", - }, - "123": { - bs: []byte{243, 126, 30, 174, 181, 95, 17, 128}, - hexString: "f37e1eaeb55f1180", - base64String: "834errVfEYA=", - }, - "你好,世界": { - bs: []byte{185, 108, 29, 112, 42, 71, 169, 240, 62, 215, 156, 154, 145, 88, 110, 10}, - hexString: "b96c1d702a47a9f03ed79c9a91586e0a", - base64String: "uWwdcCpHqfA+15yakVhuCg==", - }, - } - - for input, expect := range cases { - crypted, err := des.EncryptCBC(cryptox.PaddingPKCS7, testIV, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(crypted); err != nil { - t.Fatal(err) - } - - plain, err := des.DecryptCBC(cryptox.UnPaddingPKCS7, testIV, crypted) - if err != nil { - t.Fatal(err) - } - - if string(plain) != input { - t.Fatalf("input %s: plain %+v != input %+v", input, plain, []byte(input)) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleDESCBC$ -func TestTripleDESCBC(t *testing.T) { - des := NewTriple(testKeyTriple) - - cases := map[string]*testResult{ - "": { - bs: []byte{39, 65, 204, 186, 76, 78, 149, 112}, - hexString: "2741ccba4c4e9570", - base64String: "J0HMukxOlXA=", - }, - "123": { - bs: []byte{0, 247, 123, 125, 239, 59, 132, 68}, - hexString: "00f77b7def3b8444", - base64String: "APd7fe87hEQ=", - }, - "你好,世界": { - bs: []byte{153, 124, 242, 118, 122, 226, 179, 98, 152, 158, 80, 119, 178, 247, 19, 62}, - hexString: "997cf2767ae2b362989e5077b2f7133e", - base64String: "mXzydnris2KYnlB3svcTPg==", - }, - } - - for input, expect := range cases { - crypted, err := des.EncryptCBC(cryptox.PaddingPKCS7, testIV, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(crypted); err != nil { - t.Fatal(err) - } - - plain, err := des.DecryptCBC(cryptox.UnPaddingPKCS7, testIV, crypted) - if err != nil { - t.Fatal(err) - } - - if string(plain) != input { - t.Fatalf("input %s: plain %+v != input %+v", input, plain, []byte(input)) - } - } -} diff --git a/des/cfb.go b/des/cfb.go deleted file mode 100644 index 4380176..0000000 --- a/des/cfb.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package des - -import ( - "crypto/cipher" - - "github.com/FishGoddess/cryptox" -) - -func encryptCFB(block cipher.Block, padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - src := padding(bs.Clone(), block.BlockSize()) - dst := src.Clone() - - cipher.NewCFBEncrypter(block, iv).XORKeyStream(dst, src) - return dst, nil -} - -func decryptCFB(block cipher.Block, unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - src := bs - dst := bs.Clone() - - cipher.NewCFBDecrypter(block, iv).XORKeyStream(dst, src) - return unpadding(dst, block.BlockSize()) -} - -func (d DES) EncryptCFB(padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := d.block, d.err - if err != nil { - return nil, err - } - - return encryptCFB(block, padding, iv, bs) -} - -func (d DES) DecryptCFB(unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := d.block, d.err - if err != nil { - return nil, err - } - - return decryptCFB(block, unpadding, iv, bs) -} - -func (td TripleDES) EncryptCFB(padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := td.block, td.err - if err != nil { - return nil, err - } - - return encryptCFB(block, padding, iv, bs) -} - -func (td TripleDES) DecryptCFB(unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := td.block, td.err - if err != nil { - return nil, err - } - - return decryptCFB(block, unpadding, iv, bs) -} diff --git a/des/cfb_test.go b/des/cfb_test.go deleted file mode 100644 index cd799a9..0000000 --- a/des/cfb_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package des - -import ( - "testing" - - "github.com/FishGoddess/cryptox" -) - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestDESCFB$ -func TestDESCFB(t *testing.T) { - des := New(testKey) - - cases := map[string]*testResult{ - "": { - bs: []byte{48, 92, 56, 32, 147, 125, 156, 44}, - hexString: "305c3820937d9c2c", - base64String: "MFw4IJN9nCw=", - }, - "123": { - bs: []byte{9, 102, 3, 45, 158, 112, 145, 33}, - hexString: "0966032d9e709121", - base64String: "CWYDLZ5wkSE=", - }, - "你好,世界": { - bs: []byte{220, 233, 144, 205, 62, 200, 123, 152, 231, 237, 219, 68, 211, 43, 255, 25}, - hexString: "dce990cd3ec87b98e7eddb44d32bff19", - base64String: "3OmQzT7Ie5jn7dtE0yv/GQ==", - }, - } - - for input, expect := range cases { - crypted, err := des.EncryptCFB(cryptox.PaddingPKCS7, testIV, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(crypted); err != nil { - t.Fatal(err) - } - - plain, err := des.DecryptCFB(cryptox.UnPaddingPKCS7, testIV, crypted) - if err != nil { - t.Fatal(err) - } - - if string(plain) != input { - t.Fatalf("input %s: plain %+v != input %+v", input, plain, []byte(input)) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleDESCFB$ -func TestTripleDESCFB(t *testing.T) { - des := NewTriple(testKeyTriple) - - cases := map[string]*testResult{ - "": { - bs: []byte{92, 169, 236, 137, 11, 246, 123, 32}, - hexString: "5ca9ec890bf67b20", - base64String: "XKnsiQv2eyA=", - }, - "123": { - bs: []byte{101, 147, 215, 132, 6, 251, 118, 45}, - hexString: "6593d78406fb762d", - base64String: "ZZPXhAb7di0=", - }, - "你好,世界": { - bs: []byte{176, 28, 68, 100, 166, 67, 156, 148, 85, 69, 217, 58, 184, 136, 197, 51}, - hexString: "b01c4464a6439c945545d93ab888c533", - base64String: "sBxEZKZDnJRVRdk6uIjFMw==", - }, - } - - for input, expect := range cases { - crypted, err := des.EncryptCFB(cryptox.PaddingPKCS7, testIV, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(crypted); err != nil { - t.Fatal(err) - } - - plain, err := des.DecryptCFB(cryptox.UnPaddingPKCS7, testIV, crypted) - if err != nil { - t.Fatal(err) - } - - if string(plain) != input { - t.Fatalf("input %s: plain %+v != input %+v", input, plain, []byte(input)) - } - } -} diff --git a/des/ctr.go b/des/ctr.go deleted file mode 100644 index a95ccd2..0000000 --- a/des/ctr.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package des - -import ( - "crypto/cipher" - - "github.com/FishGoddess/cryptox" -) - -func encryptCTR(block cipher.Block, padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - src := padding(bs.Clone(), block.BlockSize()) - dst := src.Clone() - - cipher.NewCTR(block, iv).XORKeyStream(dst, src) - return dst, nil -} - -func decryptCTR(block cipher.Block, unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - src := bs - dst := bs.Clone() - - cipher.NewCTR(block, iv).XORKeyStream(dst, src) - return unpadding(dst, block.BlockSize()) -} - -func (d DES) EncryptCTR(padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := d.block, d.err - if err != nil { - return nil, err - } - - return encryptCTR(block, padding, iv, bs) -} - -func (d DES) DecryptCTR(unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := d.block, d.err - if err != nil { - return nil, err - } - - return decryptCTR(block, unpadding, iv, bs) -} - -func (td TripleDES) EncryptCTR(padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := td.block, td.err - if err != nil { - return nil, err - } - - return encryptCTR(block, padding, iv, bs) -} - -func (td TripleDES) DecryptCTR(unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := td.block, td.err - if err != nil { - return nil, err - } - - return decryptCTR(block, unpadding, iv, bs) -} diff --git a/des/ctr_test.go b/des/ctr_test.go deleted file mode 100644 index 5fcc15d..0000000 --- a/des/ctr_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package des - -import ( - "testing" - - "github.com/FishGoddess/cryptox" -) - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestDESCTR$ -func TestDESCTR(t *testing.T) { - des := New(testKey) - - cases := map[string]*testResult{ - "": { - bs: []byte{48, 92, 56, 32, 147, 125, 156, 44}, - hexString: "305c3820937d9c2c", - base64String: "MFw4IJN9nCw=", - }, - "123": { - bs: []byte{9, 102, 3, 45, 158, 112, 145, 33}, - hexString: "0966032d9e709121", - base64String: "CWYDLZ5wkSE=", - }, - "你好,世界": { - bs: []byte{220, 233, 144, 205, 62, 200, 123, 152, 82, 201, 236, 67, 30, 240, 63, 228}, - hexString: "dce990cd3ec87b9852c9ec431ef03fe4", - base64String: "3OmQzT7Ie5hSyexDHvA/5A==", - }, - } - - for input, expect := range cases { - crypted, err := des.EncryptCTR(cryptox.PaddingPKCS7, testIV, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(crypted); err != nil { - t.Fatal(err) - } - - plain, err := des.DecryptCTR(cryptox.UnPaddingPKCS7, testIV, crypted) - if err != nil { - t.Fatal(err) - } - - if string(plain) != input { - t.Fatalf("input %s: plain %+v != input %+v", input, plain, []byte(input)) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleDESCTR$ -func TestTripleDESCTR(t *testing.T) { - des := NewTriple(testKeyTriple) - - cases := map[string]*testResult{ - "": { - bs: []byte{92, 169, 236, 137, 11, 246, 123, 32}, - hexString: "5ca9ec890bf67b20", - base64String: "XKnsiQv2eyA=", - }, - "123": { - bs: []byte{101, 147, 215, 132, 6, 251, 118, 45}, - hexString: "6593d78406fb762d", - base64String: "ZZPXhAb7di0=", - }, - "你好,世界": { - bs: []byte{176, 28, 68, 100, 166, 67, 156, 148, 76, 184, 154, 31, 42, 134, 28, 205}, - hexString: "b01c4464a6439c944cb89a1f2a861ccd", - base64String: "sBxEZKZDnJRMuJofKoYczQ==", - }, - } - - for input, expect := range cases { - crypted, err := des.EncryptCTR(cryptox.PaddingPKCS7, testIV, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(crypted); err != nil { - t.Fatal(err) - } - - plain, err := des.DecryptCTR(cryptox.UnPaddingPKCS7, testIV, crypted) - if err != nil { - t.Fatal(err) - } - - if string(plain) != input { - t.Fatalf("input %s: plain %+v != input %+v", input, plain, []byte(input)) - } - } -} diff --git a/des/des.go b/des/des.go index 2fc9984..aa09225 100644 --- a/des/des.go +++ b/des/des.go @@ -7,38 +7,173 @@ package des import ( "crypto/cipher" "crypto/des" + "fmt" "github.com/FishGoddess/cryptox" ) -// DES packs some function of des. -type DES struct { - block cipher.Block - err error +func newBlock(key cryptox.Bytes) (cipher.Block, int, error) { + block, err := des.NewCipher(key) + if err != nil { + return nil, 0, err + } + + blockSize := block.BlockSize() + return block, blockSize, nil } -// New creates a new DES with key. -func New(key cryptox.Bytes) DES { - block, err := des.NewCipher(key) +func EncryptECB(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newBlock(key) + if err != nil { + return nil, err + } + + src := padding.Padding(bs, blockSize) + dst := src.Clone() + + if len(src)%blockSize != 0 { + return nil, fmt.Errorf("cryptox/des: encrypt ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) + } + + start := 0 + end := blockSize + + for end <= len(src) { + block.Encrypt(dst[start:end], src[start:end]) + + start += blockSize + end += blockSize + } + + return dst, nil +} + +func DecryptECB(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newBlock(key) + if err != nil { + return nil, err + } + + src := bs + dst := bs.Clone() + + if len(src)%blockSize != 0 { + return nil, fmt.Errorf("cryptox/des: decrypt ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) + } + + start := 0 + end := blockSize + + for end <= len(src) { + block.Decrypt(dst[start:end], src[start:end]) + + start += blockSize + end += blockSize + } + + return padding.UndoPadding(dst, blockSize) +} + +func EncryptCBC(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newBlock(key) + if err != nil { + return nil, err + } + + src := padding.Padding(bs, blockSize) + dst := src.Clone() - return DES{ - block: block, - err: err, + cipher.NewCBCEncrypter(block, iv).CryptBlocks(dst, src) + return dst, nil +} + +func DecryptCBC(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newBlock(key) + if err != nil { + return nil, err + } + + src := bs + dst := src.Clone() + + cipher.NewCBCDecrypter(block, iv).CryptBlocks(dst, src) + return padding.UndoPadding(dst, blockSize) +} + +func EncryptCFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newBlock(key) + if err != nil { + return nil, err + } + + src := padding.Padding(bs, blockSize) + dst := src.Clone() + + cipher.NewCFBEncrypter(block, iv).XORKeyStream(dst, src) + return dst, nil +} + +func DecryptCFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newBlock(key) + if err != nil { + return nil, err + } + + src := bs + dst := bs.Clone() + + cipher.NewCFBDecrypter(block, iv).XORKeyStream(dst, src) + return padding.UndoPadding(dst, blockSize) +} + +func EncryptOFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newBlock(key) + if err != nil { + return nil, err } + + src := padding.Padding(bs, blockSize) + dst := src.Clone() + + cipher.NewOFB(block, iv).XORKeyStream(dst, src) + return dst, nil } -// TripleDES packs some function of 3des. -type TripleDES struct { - block cipher.Block - err error +func DecryptOFB(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newBlock(key) + if err != nil { + return nil, err + } + + src := bs + dst := bs.Clone() + + cipher.NewOFB(block, iv).XORKeyStream(dst, src) + return padding.UndoPadding(dst, blockSize) } -// NewTriple creates a new TripleDES with key. -func NewTriple(key cryptox.Bytes) TripleDES { - block, err := des.NewTripleDESCipher(key) +func EncryptCTR(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newBlock(key) + if err != nil { + return nil, err + } + + src := padding.Padding(bs, blockSize) + dst := src.Clone() - return TripleDES{ - block: block, - err: err, + cipher.NewCTR(block, iv).XORKeyStream(dst, src) + return dst, nil +} + +func DecryptCTR(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newBlock(key) + if err != nil { + return nil, err } + + src := bs + dst := bs.Clone() + + cipher.NewCTR(block, iv).XORKeyStream(dst, src) + return padding.UndoPadding(dst, blockSize) } diff --git a/des/des_test.go b/des/des_test.go index 43a1c88..002f9c2 100644 --- a/des/des_test.go +++ b/des/des_test.go @@ -5,6 +5,7 @@ package des import ( + "crypto/des" "fmt" "testing" @@ -12,9 +13,8 @@ import ( ) var ( - testKey = []byte("12345678") - testKeyTriple = []byte("123456788765432112345678") - testIV = []byte("87654321") + testKey = []byte("12345678") + testIV = []byte("87654321") ) type testResult struct { @@ -39,16 +39,232 @@ func (tr *testResult) compareTo(bs cryptox.Bytes) error { return nil } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestDES$ -func TestDES(t *testing.T) { - if des := New(testKey); des.err != nil { - t.Fatal(des.err) +// go test -v -cover -count=1 -test.cpu=1 -run=^TestNewBlock$ +func TestNewBlock(t *testing.T) { + block, blockSize, err := newBlock(testKey) + if err != nil { + t.Fatal(err) + } + + if block == nil { + t.Fatal("block == nil") + } + + if blockSize != block.BlockSize() { + t.Fatalf("blockSize %d != block.BlockSize() %d", blockSize, block.BlockSize()) + } + + wantBlock, err := des.NewCipher(testKey) + if err != nil { + t.Fatal(err) + } + + if blockSize != wantBlock.BlockSize() { + t.Fatalf("blockSize %d != wantBlock.BlockSize() %d", blockSize, wantBlock.BlockSize()) } } -// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleDES$ -func TestTripleDES(t *testing.T) { - if des := NewTriple(testKeyTriple); des.err != nil { - t.Fatal(des.err) +// go test -v -cover -count=1 -test.cpu=1 -run=^TestECB$ +func TestECB(t *testing.T) { + cases := map[string]*testResult{ + "": { + bs: []byte{254, 185, 89, 183, 212, 100, 47, 203}, + hexString: "feb959b7d4642fcb", + base64String: "/rlZt9RkL8s=", + }, + "123": { + bs: []byte{44, 56, 133, 81, 215, 244, 137, 236}, + hexString: "2c388551d7f489ec", + base64String: "LDiFUdf0iew=", + }, + "你好,世界": { + bs: []byte{109, 82, 56, 231, 116, 36, 60, 100, 116, 149, 15, 240, 198, 38, 198, 204}, + hexString: "6d5238e774243c6474950ff0c626c6cc", + base64String: "bVI453QkPGR0lQ/wxibGzA==", + }, + } + + for input, expect := range cases { + encrypted, err := EncryptECB(testKey, cryptox.PaddingPKCS7, []byte(input)) + if err != nil { + t.Fatal(err) + } + + if err = expect.compareTo(encrypted); err != nil { + t.Fatal(err) + } + + decrypted, err := DecryptECB(testKey, cryptox.PaddingPKCS7, encrypted) + if err != nil { + t.Fatal(err) + } + + if string(decrypted) != input { + t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) + } + } +} + +// go test -v -cover -count=1 -test.cpu=1 -run=^TestCBC$ +func TestCBC(t *testing.T) { + cases := map[string]*testResult{ + "": { + bs: []byte{205, 172, 198, 131, 218, 176, 175, 188}, + hexString: "cdacc683dab0afbc", + base64String: "zazGg9qwr7w=", + }, + "123": { + bs: []byte{243, 126, 30, 174, 181, 95, 17, 128}, + hexString: "f37e1eaeb55f1180", + base64String: "834errVfEYA=", + }, + "你好,世界": { + bs: []byte{185, 108, 29, 112, 42, 71, 169, 240, 62, 215, 156, 154, 145, 88, 110, 10}, + hexString: "b96c1d702a47a9f03ed79c9a91586e0a", + base64String: "uWwdcCpHqfA+15yakVhuCg==", + }, + } + + for input, expect := range cases { + encrypted, err := EncryptCBC(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) + if err != nil { + t.Fatal(err) + } + + if err = expect.compareTo(encrypted); err != nil { + t.Fatal(err) + } + + decrypted, err := DecryptCBC(testKey, testIV, cryptox.PaddingPKCS7, encrypted) + if err != nil { + t.Fatal(err) + } + + if string(decrypted) != input { + t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) + } + } +} + +// go test -v -cover -count=1 -test.cpu=1 -run=^TestCFB$ +func TestCFB(t *testing.T) { + cases := map[string]*testResult{ + "": { + bs: []byte{48, 92, 56, 32, 147, 125, 156, 44}, + hexString: "305c3820937d9c2c", + base64String: "MFw4IJN9nCw=", + }, + "123": { + bs: []byte{9, 102, 3, 45, 158, 112, 145, 33}, + hexString: "0966032d9e709121", + base64String: "CWYDLZ5wkSE=", + }, + "你好,世界": { + bs: []byte{220, 233, 144, 205, 62, 200, 123, 152, 231, 237, 219, 68, 211, 43, 255, 25}, + hexString: "dce990cd3ec87b98e7eddb44d32bff19", + base64String: "3OmQzT7Ie5jn7dtE0yv/GQ==", + }, + } + + for input, expect := range cases { + encrypted, err := EncryptCFB(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) + if err != nil { + t.Fatal(err) + } + + if err = expect.compareTo(encrypted); err != nil { + t.Fatal(err) + } + + decrypted, err := DecryptCFB(testKey, testIV, cryptox.PaddingPKCS7, encrypted) + if err != nil { + t.Fatal(err) + } + + if string(decrypted) != input { + t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) + } + } +} + +// go test -v -cover -count=1 -test.cpu=1 -run=^TestOFB$ +func TestOFB(t *testing.T) { + cases := map[string]*testResult{ + "": { + bs: []byte{48, 92, 56, 32, 147, 125, 156, 44}, + hexString: "305c3820937d9c2c", + base64String: "MFw4IJN9nCw=", + }, + "123": { + bs: []byte{9, 102, 3, 45, 158, 112, 145, 33}, + hexString: "0966032d9e709121", + base64String: "CWYDLZ5wkSE=", + }, + "你好,世界": { + bs: []byte{220, 233, 144, 205, 62, 200, 123, 152, 169, 42, 97, 1, 193, 120, 15, 149}, + hexString: "dce990cd3ec87b98a92a6101c1780f95", + base64String: "3OmQzT7Ie5ipKmEBwXgPlQ==", + }, + } + + for input, expect := range cases { + encrypted, err := EncryptOFB(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) + if err != nil { + t.Fatal(err) + } + + if err = expect.compareTo(encrypted); err != nil { + t.Fatal(err) + } + + decrypted, err := DecryptOFB(testKey, testIV, cryptox.PaddingPKCS7, encrypted) + if err != nil { + t.Fatal(err) + } + + if string(decrypted) != input { + t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) + } + } +} + +// go test -v -cover -count=1 -test.cpu=1 -run=^TestCTR$ +func TestCTR(t *testing.T) { + cases := map[string]*testResult{ + "": { + bs: []byte{48, 92, 56, 32, 147, 125, 156, 44}, + hexString: "305c3820937d9c2c", + base64String: "MFw4IJN9nCw=", + }, + "123": { + bs: []byte{9, 102, 3, 45, 158, 112, 145, 33}, + hexString: "0966032d9e709121", + base64String: "CWYDLZ5wkSE=", + }, + "你好,世界": { + bs: []byte{220, 233, 144, 205, 62, 200, 123, 152, 82, 201, 236, 67, 30, 240, 63, 228}, + hexString: "dce990cd3ec87b9852c9ec431ef03fe4", + base64String: "3OmQzT7Ie5hSyexDHvA/5A==", + }, + } + + for input, expect := range cases { + encrypted, err := EncryptCTR(testKey, testIV, cryptox.PaddingPKCS7, []byte(input)) + if err != nil { + t.Fatal(err) + } + + if err = expect.compareTo(encrypted); err != nil { + t.Fatal(err) + } + + decrypted, err := DecryptCTR(testKey, testIV, cryptox.PaddingPKCS7, encrypted) + if err != nil { + t.Fatal(err) + } + + if string(decrypted) != input { + t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) + } } } diff --git a/des/ecb.go b/des/ecb.go deleted file mode 100644 index b7fb523..0000000 --- a/des/ecb.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package des - -import ( - "crypto/cipher" - "fmt" - - "github.com/FishGoddess/cryptox" -) - -func encryptECB(block cipher.Block, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - blockSize := block.BlockSize() - - src := padding(bs.Clone(), blockSize) - dst := src.Clone() - - if len(src)%blockSize != 0 { - return nil, fmt.Errorf("cryptox/des: encrypt ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) - } - - start := 0 - end := blockSize - - for end <= len(src) { - block.Encrypt(dst[start:end], src[start:end]) - - start += blockSize - end += blockSize - } - - return dst, nil -} - -func decryptECB(block cipher.Block, unpadding cryptox.UnPadding, bs cryptox.Bytes) (cryptox.Bytes, error) { - blockSize := block.BlockSize() - - src := bs - dst := bs.Clone() - - if len(src)%blockSize != 0 { - return nil, fmt.Errorf("cryptox/des: decrypt ecb len(src) %d %% blockSize %d != 0", len(src), blockSize) - } - - start := 0 - end := blockSize - - for end <= len(src) { - block.Decrypt(dst[start:end], src[start:end]) - - start += blockSize - end += blockSize - } - - return unpadding(dst, blockSize) -} - -func (d DES) EncryptECB(padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := d.block, d.err - if err != nil { - return nil, err - } - - return encryptECB(block, padding, bs) -} - -func (d DES) DecryptECB(unpadding cryptox.UnPadding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := d.block, d.err - if err != nil { - return nil, err - } - - return decryptECB(block, unpadding, bs) -} - -func (td TripleDES) EncryptECB(padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := td.block, td.err - if err != nil { - return nil, err - } - - return encryptECB(block, padding, bs) -} - -func (td TripleDES) DecryptECB(unpadding cryptox.UnPadding, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := td.block, td.err - if err != nil { - return nil, err - } - - return decryptECB(block, unpadding, bs) -} diff --git a/des/ecb_test.go b/des/ecb_test.go deleted file mode 100644 index 5edcd80..0000000 --- a/des/ecb_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package des - -import ( - "testing" - - "github.com/FishGoddess/cryptox" -) - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestDESECB$ -func TestDESECB(t *testing.T) { - des := New(testKey) - - cases := map[string]*testResult{ - "": { - bs: []byte{254, 185, 89, 183, 212, 100, 47, 203}, - hexString: "feb959b7d4642fcb", - base64String: "/rlZt9RkL8s=", - }, - "123": { - bs: []byte{44, 56, 133, 81, 215, 244, 137, 236}, - hexString: "2c388551d7f489ec", - base64String: "LDiFUdf0iew=", - }, - "你好,世界": { - bs: []byte{109, 82, 56, 231, 116, 36, 60, 100, 116, 149, 15, 240, 198, 38, 198, 204}, - hexString: "6d5238e774243c6474950ff0c626c6cc", - base64String: "bVI453QkPGR0lQ/wxibGzA==", - }, - } - - for input, expect := range cases { - crypted, err := des.EncryptECB(cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(crypted); err != nil { - t.Fatal(err) - } - - plain, err := des.DecryptECB(cryptox.UnPaddingPKCS7, crypted) - if err != nil { - t.Fatal(err) - } - - if string(plain) != input { - t.Fatalf("input %s: plain %+v != input %+v", input, plain, []byte(input)) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleDESECB$ -func TestTripleDESECB(t *testing.T) { - des := NewTriple(testKeyTriple) - - cases := map[string]*testResult{ - "": { - bs: []byte{163, 133, 24, 236, 31, 63, 147, 38}, - hexString: "a38518ec1f3f9326", - base64String: "o4UY7B8/kyY=", - }, - "123": { - bs: []byte{185, 2, 158, 11, 229, 10, 126, 217}, - hexString: "b9029e0be50a7ed9", - base64String: "uQKeC+UKftk=", - }, - "你好,世界": { - bs: []byte{224, 251, 123, 121, 70, 219, 201, 188, 14, 248, 74, 206, 42, 34, 16, 102}, - hexString: "e0fb7b7946dbc9bc0ef84ace2a221066", - base64String: "4Pt7eUbbybwO+ErOKiIQZg==", - }, - } - - for input, expect := range cases { - crypted, err := des.EncryptECB(cryptox.PaddingPKCS7, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(crypted); err != nil { - t.Fatal(err) - } - - plain, err := des.DecryptECB(cryptox.UnPaddingPKCS7, crypted) - if err != nil { - t.Fatal(err) - } - - if string(plain) != input { - t.Fatalf("input %s: plain %+v != input %+v", input, plain, []byte(input)) - } - } -} diff --git a/des/ofb.go b/des/ofb.go deleted file mode 100644 index 6ed082f..0000000 --- a/des/ofb.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package des - -import ( - "crypto/cipher" - - "github.com/FishGoddess/cryptox" -) - -func encryptOFB(block cipher.Block, padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - src := padding(bs.Clone(), block.BlockSize()) - dst := src.Clone() - - cipher.NewOFB(block, iv).XORKeyStream(dst, src) - return dst, nil -} - -func decryptOFB(block cipher.Block, unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - src := bs - dst := bs.Clone() - - cipher.NewOFB(block, iv).XORKeyStream(dst, src) - return unpadding(dst, block.BlockSize()) -} - -func (d DES) EncryptOFB(padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := d.block, d.err - if err != nil { - return nil, err - } - - return encryptOFB(block, padding, iv, bs) -} - -func (d DES) DecryptOFB(unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := d.block, d.err - if err != nil { - return nil, err - } - - return decryptOFB(block, unpadding, iv, bs) -} - -func (td TripleDES) EncryptOFB(padding cryptox.Padding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := td.block, td.err - if err != nil { - return nil, err - } - - return encryptOFB(block, padding, iv, bs) -} - -func (td TripleDES) DecryptOFB(unpadding cryptox.UnPadding, iv cryptox.Bytes, bs cryptox.Bytes) (cryptox.Bytes, error) { - block, err := td.block, td.err - if err != nil { - return nil, err - } - - return decryptOFB(block, unpadding, iv, bs) -} diff --git a/des/ofb_test.go b/des/ofb_test.go deleted file mode 100644 index 5518c39..0000000 --- a/des/ofb_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2024 FishGoddess. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package des - -import ( - "testing" - - "github.com/FishGoddess/cryptox" -) - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestDESOFB$ -func TestDESOFB(t *testing.T) { - des := New(testKey) - - cases := map[string]*testResult{ - "": { - bs: []byte{48, 92, 56, 32, 147, 125, 156, 44}, - hexString: "305c3820937d9c2c", - base64String: "MFw4IJN9nCw=", - }, - "123": { - bs: []byte{9, 102, 3, 45, 158, 112, 145, 33}, - hexString: "0966032d9e709121", - base64String: "CWYDLZ5wkSE=", - }, - "你好,世界": { - bs: []byte{220, 233, 144, 205, 62, 200, 123, 152, 169, 42, 97, 1, 193, 120, 15, 149}, - hexString: "dce990cd3ec87b98a92a6101c1780f95", - base64String: "3OmQzT7Ie5ipKmEBwXgPlQ==", - }, - } - - for input, expect := range cases { - crypted, err := des.EncryptOFB(cryptox.PaddingPKCS7, testIV, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(crypted); err != nil { - t.Fatal(err) - } - - plain, err := des.DecryptOFB(cryptox.UnPaddingPKCS7, testIV, crypted) - if err != nil { - t.Fatal(err) - } - - if string(plain) != input { - t.Fatalf("input %s: plain %+v != input %+v", input, plain, []byte(input)) - } - } -} - -// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleDESOFB$ -func TestTripleDESOFB(t *testing.T) { - des := NewTriple(testKeyTriple) - - cases := map[string]*testResult{ - "": { - bs: []byte{92, 169, 236, 137, 11, 246, 123, 32}, - hexString: "5ca9ec890bf67b20", - base64String: "XKnsiQv2eyA=", - }, - "123": { - bs: []byte{101, 147, 215, 132, 6, 251, 118, 45}, - hexString: "6593d78406fb762d", - base64String: "ZZPXhAb7di0=", - }, - "你好,世界": { - bs: []byte{176, 28, 68, 100, 166, 67, 156, 148, 46, 244, 26, 37, 38, 97, 62, 68}, - hexString: "b01c4464a6439c942ef41a2526613e44", - base64String: "sBxEZKZDnJQu9BolJmE+RA==", - }, - } - - for input, expect := range cases { - crypted, err := des.EncryptOFB(cryptox.PaddingPKCS7, testIV, []byte(input)) - if err != nil { - t.Fatal(err) - } - - if err = expect.compareTo(crypted); err != nil { - t.Fatal(err) - } - - plain, err := des.DecryptOFB(cryptox.UnPaddingPKCS7, testIV, crypted) - if err != nil { - t.Fatal(err) - } - - if string(plain) != input { - t.Fatalf("input %s: plain %+v != input %+v", input, plain, []byte(input)) - } - } -} diff --git a/des/triple_des.go b/des/triple_des.go new file mode 100644 index 0000000..4d66b1e --- /dev/null +++ b/des/triple_des.go @@ -0,0 +1,179 @@ +// Copyright 2024 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package des + +import ( + "crypto/cipher" + "crypto/des" + "fmt" + + "github.com/FishGoddess/cryptox" +) + +func newTripleBlock(key cryptox.Bytes) (cipher.Block, int, error) { + block, err := des.NewTripleDESCipher(key) + if err != nil { + return nil, 0, err + } + + blockSize := block.BlockSize() + return block, blockSize, nil +} + +func EncryptECBTriple(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newTripleBlock(key) + if err != nil { + return nil, err + } + + src := padding.Padding(bs, blockSize) + dst := src.Clone() + + if len(src)%blockSize != 0 { + return nil, fmt.Errorf("cryptox/des: encrypt ecb triple len(src) %d %% blockSize %d != 0", len(src), blockSize) + } + + start := 0 + end := blockSize + + for end <= len(src) { + block.Encrypt(dst[start:end], src[start:end]) + + start += blockSize + end += blockSize + } + + return dst, nil +} + +func DecryptECBTriple(key cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newTripleBlock(key) + if err != nil { + return nil, err + } + + src := bs + dst := bs.Clone() + + if len(src)%blockSize != 0 { + return nil, fmt.Errorf("cryptox/des: decrypt ecb triple len(src) %d %% blockSize %d != 0", len(src), blockSize) + } + + start := 0 + end := blockSize + + for end <= len(src) { + block.Decrypt(dst[start:end], src[start:end]) + + start += blockSize + end += blockSize + } + + return padding.UndoPadding(dst, blockSize) +} + +func EncryptCBCTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newTripleBlock(key) + if err != nil { + return nil, err + } + + src := padding.Padding(bs, blockSize) + dst := src.Clone() + + cipher.NewCBCEncrypter(block, iv).CryptBlocks(dst, src) + return dst, nil +} + +func DecryptCBCTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newTripleBlock(key) + if err != nil { + return nil, err + } + + src := bs + dst := src.Clone() + + cipher.NewCBCDecrypter(block, iv).CryptBlocks(dst, src) + return padding.UndoPadding(dst, blockSize) +} + +func EncryptCFBTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newTripleBlock(key) + if err != nil { + return nil, err + } + + src := padding.Padding(bs, blockSize) + dst := src.Clone() + + cipher.NewCFBEncrypter(block, iv).XORKeyStream(dst, src) + return dst, nil +} + +func DecryptCFBTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newTripleBlock(key) + if err != nil { + return nil, err + } + + src := bs + dst := bs.Clone() + + cipher.NewCFBDecrypter(block, iv).XORKeyStream(dst, src) + return padding.UndoPadding(dst, blockSize) +} + +func EncryptOFBTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newTripleBlock(key) + if err != nil { + return nil, err + } + + src := padding.Padding(bs, blockSize) + dst := src.Clone() + + cipher.NewOFB(block, iv).XORKeyStream(dst, src) + return dst, nil +} + +func DecryptOFBTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newTripleBlock(key) + if err != nil { + return nil, err + } + + src := bs + dst := bs.Clone() + + cipher.NewOFB(block, iv).XORKeyStream(dst, src) + return padding.UndoPadding(dst, blockSize) +} + +func EncryptCTRTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newTripleBlock(key) + if err != nil { + return nil, err + } + + src := padding.Padding(bs, blockSize) + dst := src.Clone() + + cipher.NewCTR(block, iv).XORKeyStream(dst, src) + return dst, nil +} + +func DecryptCTRTriple(key cryptox.Bytes, iv cryptox.Bytes, padding cryptox.Padding, bs cryptox.Bytes) (cryptox.Bytes, error) { + block, blockSize, err := newTripleBlock(key) + if err != nil { + return nil, err + } + + src := bs + dst := bs.Clone() + + cipher.NewCTR(block, iv).XORKeyStream(dst, src) + return padding.UndoPadding(dst, blockSize) +} diff --git a/des/triple_des_test.go b/des/triple_des_test.go new file mode 100644 index 0000000..a295613 --- /dev/null +++ b/des/triple_des_test.go @@ -0,0 +1,246 @@ +// Copyright 2024 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package des + +import ( + "crypto/des" + "testing" + + "github.com/FishGoddess/cryptox" +) + +var ( + testTripleKey = []byte("123456788765432112345678") +) + +// go test -v -cover -count=1 -test.cpu=1 -run=^TestNewTripleBlock$ +func TestNewTripleBlock(t *testing.T) { + block, blockSize, err := newTripleBlock(testTripleKey) + if err != nil { + t.Fatal(err) + } + + if block == nil { + t.Fatal("block == nil") + } + + if blockSize != block.BlockSize() { + t.Fatalf("blockSize %d != block.BlockSize() %d", blockSize, block.BlockSize()) + } + + wantBlock, err := des.NewTripleDESCipher(testTripleKey) + if err != nil { + t.Fatal(err) + } + + if blockSize != wantBlock.BlockSize() { + t.Fatalf("blockSize %d != wantBlock.BlockSize() %d", blockSize, wantBlock.BlockSize()) + } +} + +// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleECB$ +func TestTripleECB(t *testing.T) { + cases := map[string]*testResult{ + "": { + bs: []byte{163, 133, 24, 236, 31, 63, 147, 38}, + hexString: "a38518ec1f3f9326", + base64String: "o4UY7B8/kyY=", + }, + "123": { + bs: []byte{185, 2, 158, 11, 229, 10, 126, 217}, + hexString: "b9029e0be50a7ed9", + base64String: "uQKeC+UKftk=", + }, + "你好,世界": { + bs: []byte{224, 251, 123, 121, 70, 219, 201, 188, 14, 248, 74, 206, 42, 34, 16, 102}, + hexString: "e0fb7b7946dbc9bc0ef84ace2a221066", + base64String: "4Pt7eUbbybwO+ErOKiIQZg==", + }, + } + + for input, expect := range cases { + encrypted, err := EncryptECBTriple(testTripleKey, cryptox.PaddingPKCS7, []byte(input)) + if err != nil { + t.Fatal(err) + } + + if err = expect.compareTo(encrypted); err != nil { + t.Fatal(err) + } + + decrypted, err := DecryptECBTriple(testTripleKey, cryptox.PaddingPKCS7, encrypted) + if err != nil { + t.Fatal(err) + } + + if string(decrypted) != input { + t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) + } + } +} + +// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleCBC$ +func TestTripleCBC(t *testing.T) { + cases := map[string]*testResult{ + "": { + bs: []byte{39, 65, 204, 186, 76, 78, 149, 112}, + hexString: "2741ccba4c4e9570", + base64String: "J0HMukxOlXA=", + }, + "123": { + bs: []byte{0, 247, 123, 125, 239, 59, 132, 68}, + hexString: "00f77b7def3b8444", + base64String: "APd7fe87hEQ=", + }, + "你好,世界": { + bs: []byte{153, 124, 242, 118, 122, 226, 179, 98, 152, 158, 80, 119, 178, 247, 19, 62}, + hexString: "997cf2767ae2b362989e5077b2f7133e", + base64String: "mXzydnris2KYnlB3svcTPg==", + }, + } + + for input, expect := range cases { + encrypted, err := EncryptCBCTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, []byte(input)) + if err != nil { + t.Fatal(err) + } + + if err = expect.compareTo(encrypted); err != nil { + t.Fatal(err) + } + + decrypted, err := DecryptCBCTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, encrypted) + if err != nil { + t.Fatal(err) + } + + if string(decrypted) != input { + t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) + } + } +} + +// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleCFB$ +func TestTripleCFB(t *testing.T) { + cases := map[string]*testResult{ + "": { + bs: []byte{92, 169, 236, 137, 11, 246, 123, 32}, + hexString: "5ca9ec890bf67b20", + base64String: "XKnsiQv2eyA=", + }, + "123": { + bs: []byte{101, 147, 215, 132, 6, 251, 118, 45}, + hexString: "6593d78406fb762d", + base64String: "ZZPXhAb7di0=", + }, + "你好,世界": { + bs: []byte{176, 28, 68, 100, 166, 67, 156, 148, 85, 69, 217, 58, 184, 136, 197, 51}, + hexString: "b01c4464a6439c945545d93ab888c533", + base64String: "sBxEZKZDnJRVRdk6uIjFMw==", + }, + } + + for input, expect := range cases { + encrypted, err := EncryptCFBTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, []byte(input)) + if err != nil { + t.Fatal(err) + } + + if err = expect.compareTo(encrypted); err != nil { + t.Fatal(err) + } + + decrypted, err := DecryptCFBTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, encrypted) + if err != nil { + t.Fatal(err) + } + + if string(decrypted) != input { + t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) + } + } +} + +// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleOFB$ +func TestTripleOFB(t *testing.T) { + cases := map[string]*testResult{ + "": { + bs: []byte{92, 169, 236, 137, 11, 246, 123, 32}, + hexString: "5ca9ec890bf67b20", + base64String: "XKnsiQv2eyA=", + }, + "123": { + bs: []byte{101, 147, 215, 132, 6, 251, 118, 45}, + hexString: "6593d78406fb762d", + base64String: "ZZPXhAb7di0=", + }, + "你好,世界": { + bs: []byte{176, 28, 68, 100, 166, 67, 156, 148, 46, 244, 26, 37, 38, 97, 62, 68}, + hexString: "b01c4464a6439c942ef41a2526613e44", + base64String: "sBxEZKZDnJQu9BolJmE+RA==", + }, + } + + for input, expect := range cases { + encrypted, err := EncryptOFBTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, []byte(input)) + if err != nil { + t.Fatal(err) + } + + if err = expect.compareTo(encrypted); err != nil { + t.Fatal(err) + } + + decrypted, err := DecryptOFBTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, encrypted) + if err != nil { + t.Fatal(err) + } + + if string(decrypted) != input { + t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) + } + } +} + +// go test -v -cover -count=1 -test.cpu=1 -run=^TestTripleCTR$ +func TestTripleCTR(t *testing.T) { + cases := map[string]*testResult{ + "": { + bs: []byte{92, 169, 236, 137, 11, 246, 123, 32}, + hexString: "5ca9ec890bf67b20", + base64String: "XKnsiQv2eyA=", + }, + "123": { + bs: []byte{101, 147, 215, 132, 6, 251, 118, 45}, + hexString: "6593d78406fb762d", + base64String: "ZZPXhAb7di0=", + }, + "你好,世界": { + bs: []byte{176, 28, 68, 100, 166, 67, 156, 148, 76, 184, 154, 31, 42, 134, 28, 205}, + hexString: "b01c4464a6439c944cb89a1f2a861ccd", + base64String: "sBxEZKZDnJRMuJofKoYczQ==", + }, + } + + for input, expect := range cases { + encrypted, err := EncryptCTRTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, []byte(input)) + if err != nil { + t.Fatal(err) + } + + if err = expect.compareTo(encrypted); err != nil { + t.Fatal(err) + } + + decrypted, err := DecryptCTRTriple(testTripleKey, testIV, cryptox.PaddingPKCS7, encrypted) + if err != nil { + t.Fatal(err) + } + + if string(decrypted) != input { + t.Fatalf("input %s: decrypted %+v != input %+v", input, decrypted, []byte(input)) + } + } +}