Skip to content

Commit

Permalink
优化
Browse files Browse the repository at this point in the history
  • Loading branch information
deatil committed May 13, 2024
1 parent ee2625e commit 4b5139c
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 34 deletions.
58 changes: 26 additions & 32 deletions cipher/rabbitio/rabbit.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,15 @@ import (
"errors"
"math/bits"
"crypto/cipher"
"encoding/binary"

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

var (
invalidKeyLenErr = errors.New("cryptobin/rabbitio: key must be 16 byte len, not more not less")
invalidIVXLenErr = errors.New("cryptobin/rabbitio: iv must be 8 byte len or nothing (zero) at all")
errInvalidKeyLen = errors.New("cryptobin/rabbitio: key must be 16 byte len, not more not less")
errInvalidIVXLen = errors.New("cryptobin/rabbitio: iv must be 8 byte len or nothing (zero) at all")
)

var aro = []uint32{
0x4D34D34D, 0xD34D34D3, 0x34D34D34, 0x4D34D34D,
0xD34D34D3, 0x34D34D34, 0x4D34D34D, 0xD34D34D3,
}

type rabbitCipher struct {
xbit [8]uint32
cbit [8]uint32
Expand All @@ -32,10 +26,10 @@ type rabbitCipher struct {
// must be either zero len or 8 byte len, error will be returned on wrong key/iv len
func NewCipher(key []byte, iv []byte) (cipher.Stream, error) {
if len(key) != 16 {
return nil, invalidKeyLenErr
return nil, errInvalidKeyLen
}
if len(iv) != 0 && len(iv) != 8 {
return nil, invalidIVXLenErr
return nil, errInvalidIVXLen
}

c := new(rabbitCipher)
Expand All @@ -56,11 +50,12 @@ func (r *rabbitCipher) XORKeyStream(dst, src []byte) {
if alias.InexactOverlap(dst[:len(src)], src) {
panic("cryptobin/rabbit: invalid buffer overlap")
}

for i := range src {
if len(r.ks) == 0 {
r.extract()
}

dst[i] = src[i] ^ r.ks[0]
r.ks = r.ks[1:]
}
Expand All @@ -69,15 +64,15 @@ func (r *rabbitCipher) XORKeyStream(dst, src []byte) {
func (r *rabbitCipher) expandKey(key, iv []byte) {
var k [4]uint32
for i := range k {
k[i] = binary.LittleEndian.Uint32(key[i*4:])
k[i] = getu32(key[i*4:])
}

r.setupKey(k[:])

if len(iv) != 0 {
var v [4]uint16
for i := range v {
v[i] = binary.LittleEndian.Uint16(iv[i*2:])
v[i] = getu16(iv[i*2:])
}

r.setupIV(v[:])
Expand All @@ -93,17 +88,19 @@ func (r *rabbitCipher) setupKey(key []uint32) {
r.xbit[5] = key[1]<<16 | key[0]>>16
r.xbit[6] = key[3]
r.xbit[7] = key[2]<<16 | key[1]>>16
r.cbit[0] = bits.RotateLeft32(key[2], 16)
r.cbit[0] = rol32(key[2], 16)
r.cbit[1] = key[0]&0xffff0000 | key[1]&0xffff
r.cbit[2] = bits.RotateLeft32(key[3], 16)
r.cbit[2] = rol32(key[3], 16)
r.cbit[3] = key[1]&0xffff0000 | key[2]&0xffff
r.cbit[4] = bits.RotateLeft32(key[0], 16)
r.cbit[4] = rol32(key[0], 16)
r.cbit[5] = key[2]&0xffff0000 | key[3]&0xffff
r.cbit[6] = bits.RotateLeft32(key[1], 16)
r.cbit[6] = rol32(key[1], 16)
r.cbit[7] = key[3]&0xffff0000 | key[0]&0xffff

for i := 0; i < 4; i++ {
r.nextState()
}

r.cbit[0] ^= r.xbit[4]
r.cbit[1] ^= r.xbit[5]
r.cbit[2] ^= r.xbit[6]
Expand All @@ -123,6 +120,7 @@ func (r *rabbitCipher) setupIV(iv []uint16) {
r.cbit[5] ^= uint32(iv[3])<<16 | uint32(iv[1])
r.cbit[6] ^= uint32(iv[3])<<16 | uint32(iv[2])
r.cbit[7] ^= uint32(iv[2])<<16 | uint32(iv[0])

for i := 0; i < 4; i++ {
r.nextState()
}
Expand All @@ -133,17 +131,19 @@ func (r *rabbitCipher) nextState() {
for i := range r.cbit {
r.carry, r.cbit[i] = bits.Sub32(aro[i], r.cbit[i], r.carry)
}

for i := range GRX {
GRX[i] = gfunction(r.xbit[i], r.cbit[i])
}
r.xbit[0x00] = GRX[0] + bits.RotateLeft32(GRX[7], 16) + bits.RotateLeft32(GRX[6], 16)
r.xbit[0x01] = GRX[1] + bits.RotateLeft32(GRX[0], 8) + GRX[7]
r.xbit[0x02] = GRX[2] + bits.RotateLeft32(GRX[1], 16) + bits.RotateLeft32(GRX[0], 16)
r.xbit[0x03] = GRX[3] + bits.RotateLeft32(GRX[2], 8) + GRX[1]
r.xbit[0x04] = GRX[4] + bits.RotateLeft32(GRX[3], 16) + bits.RotateLeft32(GRX[2], 16)
r.xbit[0x05] = GRX[5] + bits.RotateLeft32(GRX[4], 8) + GRX[3]
r.xbit[0x06] = GRX[6] + bits.RotateLeft32(GRX[5], 16) + bits.RotateLeft32(GRX[4], 16)
r.xbit[0x07] = GRX[7] + bits.RotateLeft32(GRX[6], 8) + GRX[5]

r.xbit[0x00] = GRX[0] + rol32(GRX[7], 16) + rol32(GRX[6], 16)
r.xbit[0x01] = GRX[1] + rol32(GRX[0], 8) + GRX[7]
r.xbit[0x02] = GRX[2] + rol32(GRX[1], 16) + rol32(GRX[0], 16)
r.xbit[0x03] = GRX[3] + rol32(GRX[2], 8) + GRX[1]
r.xbit[0x04] = GRX[4] + rol32(GRX[3], 16) + rol32(GRX[2], 16)
r.xbit[0x05] = GRX[5] + rol32(GRX[4], 8) + GRX[3]
r.xbit[0x06] = GRX[6] + rol32(GRX[5], 16) + rol32(GRX[4], 16)
r.xbit[0x07] = GRX[7] + rol32(GRX[6], 8) + GRX[5]
}

func (r *rabbitCipher) extract() {
Expand All @@ -156,14 +156,8 @@ func (r *rabbitCipher) extract() {
sw[3] = r.xbit[6] ^ (r.xbit[3]>>16 | r.xbit[1]<<16)

for i := range sw {
binary.LittleEndian.PutUint32(r.sbit[i*4:], sw[i])
putu32(r.sbit[i*4:], sw[i])
}

r.ks = r.sbit[:]
}

func gfunction(u, v uint32) uint32 {
uv := uint64(u + v)
uv *= uv
return uint32(uv>>32) ^ uint32(uv)
}
67 changes: 65 additions & 2 deletions cipher/rabbitio/rabbit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package rabbitio_test

import (
"fmt"
"bytes"
"testing"
"encoding/hex"

"github.com/deatil/go-cryptobin/cipher/rabbitio"
)

func TestNewCipher(t *testing.T) {
func Test_NewCipher(t *testing.T) {
key, ivt := []byte("12345678abcdefgh"), []byte("1234qwer")
txt := "test NewReadercipher text dummy tx"
cph, err := rabbitio.NewCipher(key, ivt)
Expand All @@ -30,7 +32,68 @@ func TestNewCipher(t *testing.T) {

}

func BenchmarkNewCipher(b *testing.B) {
var testDatas = []struct {
key string
iv string
plain string
cipher string
} {
{
"12345678901234561234567890123456",
"",
"0000000000000000",
"d55293bba40cadf0",
},
{
"12345678901234561234567890123456",
"1234567812345678",
"0000000000000000",
"da7671fb2e61c40a",
},
{
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"1234567812345678",
"0000000000000000",
"22e8c9583f950d46",
},
}

func Test_Check(t *testing.T) {
for _, v := range testDatas {
keyBytes, _ := hex.DecodeString(v.key)
ivBytes, _ := hex.DecodeString(v.iv)
plainBytes, _ := hex.DecodeString(v.plain)
cipherBytes, _ := hex.DecodeString(v.cipher)

c, err := rabbitio.NewCipher(keyBytes, ivBytes)
if err != nil {
t.Fatal(err.Error())
}

var encrypted []byte = make([]byte, len(plainBytes))
c.XORKeyStream(encrypted[:], plainBytes)

if !bytes.Equal(encrypted, cipherBytes) {
t.Errorf("encryption/decryption failed: got %x, want %x", encrypted, cipherBytes)
}

// =================

c2, err := rabbitio.NewCipher(keyBytes, ivBytes)
if err != nil {
t.Fatal(err.Error())
}

var decrypted []byte = make([]byte, len(cipherBytes))
c2.XORKeyStream(decrypted[:], cipherBytes)

if !bytes.Equal(decrypted, plainBytes) {
t.Errorf("encryption/decryption failed: got %x, want %x", decrypted, plainBytes)
}
}
}

func Benchmark_NewCipher(b *testing.B) {
b.Run(fmt.Sprintf("bench %v", b.N), func(b *testing.B) {
for i := 0; i < b.N; i++ {
key, ivt := []byte("12345678abcdefgh"), []byte("1234qwer")
Expand Down
6 changes: 6 additions & 0 deletions cipher/rabbitio/sbox.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package rabbitio

var aro = []uint32{
0x4D34D34D, 0xD34D34D3, 0x34D34D34, 0x4D34D34D,
0xD34D34D3, 0x34D34D34, 0x4D34D34D, 0xD34D34D3,
}
43 changes: 43 additions & 0 deletions cipher/rabbitio/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package rabbitio

import (
"math/bits"
"encoding/binary"
)

// Endianness option
const littleEndian bool = true

func getu16(ptr []byte) uint16 {
if littleEndian {
return binary.LittleEndian.Uint16(ptr)
} else {
return binary.BigEndian.Uint16(ptr)
}
}

func getu32(ptr []byte) uint32 {
if littleEndian {
return binary.LittleEndian.Uint32(ptr)
} else {
return binary.BigEndian.Uint32(ptr)
}
}

func putu32(ptr []byte, a uint32) {
if littleEndian {
binary.LittleEndian.PutUint32(ptr, a)
} else {
binary.BigEndian.PutUint32(ptr, a)
}
}

func rol32(x uint32, n int) uint32 {
return bits.RotateLeft32(x, n)
}

func gfunction(u, v uint32) uint32 {
uv := uint64(u + v)
uv *= uv
return uint32(uv>>32) ^ uint32(uv)
}

0 comments on commit 4b5139c

Please sign in to comment.