Skip to content

Commit

Permalink
优化
Browse files Browse the repository at this point in the history
  • Loading branch information
deatil committed Mar 20, 2024
1 parent e249c8f commit 94ae38b
Show file tree
Hide file tree
Showing 9 changed files with 368 additions and 228 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ ret := crypto.
* 设置向量:
`SetIv(data string)`, `WithIv(iv []byte)`
* 加密类型:
`Aes()`, `Des()`, `TripleDes()`, `Twofish()`, `Blowfish()`, `Tea(rounds ...int)`, `Xtea()`, `Cast5()`, `RC4()`, `Idea()`, `SM4()`, `Chacha20(nonce string, counter ...uint32)`, `Chacha20poly1305(nonce string, additional string)`, `Xts(cipher string, sectorNum uint64)`
`Aes()`, `Des()`, `TripleDes()`, `Twofish()`, `Blowfish()`, `Tea(rounds ...int)`, `Xtea()`, `Cast5()`, `RC4()`, `Idea()`, `SM4()`, `Chacha20(counter ...uint32)`, `Chacha20poly1305(additional ...[]byte)`, `Xts(cipher string, sectorNum uint64)`
* 加密模式:
`ECB()`, `CBC()`, `PCBC()`, `CFB()`, `OFB()`, `CTR()`, `GCM(nonce string, additional ...string)`, `CCM(nonce string, additional ...string)`
`ECB()`, `CBC()`, `PCBC()`, `CFB()`, `OFB()`, `CTR()`, `GCM(additional ...[]byte)`, `CCM(additional ...[]byte)`
* 补码方式:
`NoPadding()`, `ZeroPadding()`, `PKCS5Padding()`, `PKCS7Padding()`, `X923Padding()`, `ISO10126Padding()`, `ISO7816_4Padding()`,`ISO97971Padding()`,`PBOC2Padding()`, `TBCPadding()`, `PKCS1Padding(bt ...string)`
* 操作类型:
Expand Down
73 changes: 44 additions & 29 deletions cipher/ccm/ccm.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import (
"math"
"errors"
"encoding/binary"
"crypto/subtle"
go_cipher "crypto/cipher"
go_subtle "crypto/subtle"

"github.com/deatil/go-cryptobin/tool/xor"
"github.com/deatil/go-cryptobin/tool/alias"
)

Expand All @@ -18,6 +18,8 @@ const (
ccmStandardNonceSize = 12
)

var errOpen = errors.New("cipher: message authentication failed")

// ccmAble is an interface implemented by ciphers that have a specific optimized
// implementation of CCM.
type ccmAble interface {
Expand All @@ -30,29 +32,6 @@ type ccm struct {
tagSize int
}

func (c *ccm) NonceSize() int {
return c.nonceSize
}

func (c *ccm) Overhead() int {
return c.tagSize
}

func (c *ccm) MaxLength() int {
return maxlen(15-c.NonceSize(), c.Overhead())
}

func maxlen(L, tagsize int) int {
max := (uint64(1) << (8 * L)) - 1
if m64 := uint64(math.MaxInt64) - uint64(tagsize); L > 8 || max > m64 {
max = m64 // The maximum lentgh on a 64bit arch
}
if max != uint64(int(max)) {
return math.MaxInt32 - tagsize // We have only 32bit int's
}
return int(max)
}

// NewCCM returns the given 128-bit, block cipher wrapped in CCM
// with the standard nonce length.
func NewCCM(cipher go_cipher.Block) (go_cipher.AEAD, error) {
Expand Down Expand Up @@ -103,6 +82,31 @@ func NewCCMWithNonceAndTagSize(cipher go_cipher.Block, nonceSize, tagSize int) (
return c, nil
}

func (c *ccm) NonceSize() int {
return c.nonceSize
}

func (c *ccm) Overhead() int {
return c.tagSize
}

func (c *ccm) MaxLength() int {
return maxlen(15-c.NonceSize(), c.Overhead())
}

func maxlen(L, tagsize int) int {
max := (uint64(1) << (8 * L)) - 1
if m64 := uint64(math.MaxInt64) - uint64(tagsize); L > 8 || max > m64 {
max = m64 // The maximum lentgh on a 64bit arch
}

if max != uint64(int(max)) {
return math.MaxInt32 - tagsize // We have only 32bit int's
}

return int(max)
}

// https://tools.ietf.org/html/rfc3610
func (c *ccm) deriveCounter(counter *[ccmBlockSize]byte, nonce []byte) {
counter[0] = byte(14 - c.nonceSize)
Expand All @@ -111,14 +115,15 @@ func (c *ccm) deriveCounter(counter *[ccmBlockSize]byte, nonce []byte) {

func (c *ccm) cmac(out, data []byte) {
for len(data) >= ccmBlockSize {
xor.XorBytes(out, out, data)
subtle.XORBytes(out, out, data)
c.cipher.Encrypt(out, out)
data = data[ccmBlockSize:]
}

if len(data) > 0 {
var block [ccmBlockSize]byte
copy(block[:], data)
xor.XorBytes(out, out, data)
subtle.XORBytes(out, out, data)
c.cipher.Encrypt(out, out)
}
}
Expand All @@ -129,10 +134,12 @@ func (c *ccm) auth(nonce, plaintext, additionalData []byte, tagMask *[ccmBlockSi
if len(additionalData) > 0 {
out[0] = 1 << 6 // 64*Adata
}

out[0] |= byte(c.tagSize-2) << 2 // M' = ((tagSize - 2) / 2)*8
out[0] |= byte(14 - c.nonceSize) // L'
binary.BigEndian.PutUint64(out[ccmBlockSize-8:], uint64(len(plaintext)))
copy(out[1:], nonce)

// B0
c.cipher.Encrypt(out[:], out[:])

Expand Down Expand Up @@ -160,24 +167,29 @@ func (c *ccm) auth(nonce, plaintext, additionalData []byte, tagMask *[ccmBlockSi
binary.BigEndian.PutUint64(block[2:i], uint64(n))
}
}

i = copy(block[i:], additionalData) // first block start with additional data length
c.cmac(out[:], block[:])
c.cmac(out[:], additionalData[i:])
}

if len(plaintext) > 0 {
c.cmac(out[:], plaintext)
}
xor.XorWords(out[:], out[:], tagMask[:])

subtle.XORBytes(out[:], out[:], tagMask[:])
return out[:c.tagSize]
}

func (c *ccm) Seal(dst, nonce, plaintext, data []byte) []byte {
if len(nonce) != c.nonceSize {
panic("cryptobin/ccm: incorrect nonce length given to CCM")
}

if uint64(len(plaintext)) > uint64(c.MaxLength()) {
panic("cryptobin/ccm: message too large for CCM")
}

ret, out := alias.SliceForAppend(dst, len(plaintext)+c.tagSize)
if alias.InexactOverlap(out, plaintext) {
panic("cryptobin/ccm: invalid buffer overlap")
Expand All @@ -197,12 +209,11 @@ func (c *ccm) Seal(dst, nonce, plaintext, data []byte) []byte {
return ret
}

var errOpen = errors.New("cipher: message authentication failed")

func (c *ccm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
if len(nonce) != c.nonceSize {
panic("cryptobin/ccm: incorrect nonce length given to CCM")
}

// Sanity check to prevent the authentication from always succeeding if an implementation
// leaves tagSize uninitialized, for example.
if c.tagSize < ccmMinimumTagSize {
Expand Down Expand Up @@ -230,8 +241,10 @@ func (c *ccm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
}

counter[len(counter)-1] |= 1

ctr := go_cipher.NewCTR(c.cipher, counter[:])
ctr.XORKeyStream(out, ciphertext)

expectedTag := c.auth(nonce, out, data, &tagMask)
if go_subtle.ConstantTimeCompare(expectedTag, tag) != 1 {
// The AESNI code decrypts and authenticates concurrently, and
Expand All @@ -241,7 +254,9 @@ func (c *ccm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
for i := range out {
out[i] = 0
}

return nil, errOpen
}

return ret, nil
}
84 changes: 84 additions & 0 deletions cipher/ccm/ccm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package ccm

import (
"bytes"
"testing"
"crypto/aes"
)

func Test_Check(t *testing.T) {
c4a := make([]byte, 524288/8)
for i := range c4a {
c4a[i] = byte(i)
}

tests := []struct {
Key []byte
Nonce []byte
Data []byte
PlainText []byte
CipherText []byte
TagLen int
}{
{ // C.1
[]byte{0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f},
[]byte{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
[]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07},
[]byte{0x20, 0x21, 0x22, 0x23},
[]byte{0x71, 0x62, 0x01, 0x5b, 0x4d, 0xac, 0x25, 0x5d},
4,
},
{ // C.2
[]byte{0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f},
[]byte{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
[]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
[]byte{0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f},
[]byte{0xd2, 0xa1, 0xf0, 0xe0, 0x51, 0xea, 0x5f, 0x62, 0x08, 0x1a, 0x77, 0x92, 0x07, 0x3d, 0x59, 0x3d, 0x1f, 0xc6, 0x4f, 0xbf, 0xac, 0xcd},
6,
},
{ // C.3
[]byte{0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f},
[]byte{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b},
[]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13},
[]byte{0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37},

[]byte{0xe3, 0xb2, 0x01, 0xa9, 0xf5, 0xb7, 0x1a, 0x7a, 0x9b, 0x1c, 0xea, 0xec, 0xcd, 0x97, 0xe7, 0x0b, 0x61, 0x76, 0xaa, 0xd9, 0xa4, 0x42, 0x8a, 0xa5, 0x48, 0x43, 0x92, 0xfb, 0xc1, 0xb0, 0x99, 0x51},
8,
},
{ // C.4
[]byte{0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f},
[]byte{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c},
c4a,
[]byte{0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f},

[]byte{0x69, 0x91, 0x5d, 0xad, 0x1e, 0x84, 0xc6, 0x37, 0x6a, 0x68, 0xc2, 0x96, 0x7e, 0x4d, 0xab, 0x61, 0x5a, 0xe0, 0xfd, 0x1f, 0xae, 0xc4, 0x4c, 0xc4, 0x84, 0x82, 0x85, 0x29, 0x46, 0x3c, 0xcf, 0x72, 0xb4, 0xac, 0x6b, 0xec, 0x93, 0xe8, 0x59, 0x8e, 0x7f, 0x0d, 0xad, 0xbc, 0xea, 0x5b},
14,
},
}

for _, test := range tests {
c, err := aes.NewCipher(test.Key)
if err != nil {
t.Fatal(err)
}

aesCCM, err := NewCCMWithNonceAndTagSize(c, len(test.Nonce), test.TagLen)
if err != nil {
t.Fatal(err)
}

cipherText := aesCCM.Seal(nil, test.Nonce, test.PlainText, test.Data)
if !bytes.Equal(test.CipherText, cipherText) {
t.Errorf("cipher text doesn't match")
}

plainText, err := aesCCM.Open(nil, test.Nonce, test.CipherText, test.Data)
if err != nil {
t.Fatal(err)
}

if !bytes.Equal(test.PlainText, plainText) {
t.Errorf("plain text doesn't match")
}
}
}
2 changes: 1 addition & 1 deletion cryptobin/crypto/cryptobin.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func NewCryptobin() Cryptobin {
}

// 构造函数
// New
// New Cryptobin
func New() Cryptobin {
return NewCryptobin()
}
Expand Down
Loading

0 comments on commit 94ae38b

Please sign in to comment.