aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSina Ghaderi <khokooli@gmail.com>2022-01-23 07:17:19 +0330
committerSina Ghaderi <khokooli@gmail.com>2022-01-23 07:17:19 +0330
commit42b54e4394977c34c8cf738a1d19f9b37a6a8c5e (patch)
treeab60feff9575cf9b6d9507b59aa45c72baf0c77c
add source code
-rw-r--r--go.mod3
-rw-r--r--io_test.go105
-rw-r--r--iowrp.go26
-rw-r--r--rabbit.go144
4 files changed, 278 insertions, 0 deletions
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..9c472c0
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module snix.ir/rabbitio
+
+go 1.17
diff --git a/io_test.go b/io_test.go
new file mode 100644
index 0000000..582dd1e
--- /dev/null
+++ b/io_test.go
@@ -0,0 +1,105 @@
+package rabbitio_test
+
+import (
+ "bytes"
+ "encoding/hex"
+ "io"
+ "strings"
+ "testing"
+
+ "snix.ir/rabbitio"
+)
+
+func TestNewWriterCipher(t *testing.T) {
+ key, ivt := []byte("12345678abcdefgh"), []byte("1234qwer")
+ txt := "dummy text to test NewWriterCipher"
+ twr := strings.NewReader(txt)
+
+ iw := new(bytes.Buffer)
+
+ t.Logf("encrypting plain text ---")
+ cw, err := rabbitio.NewWriterCipher(key, ivt, iw)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if _, err := io.Copy(cw, twr); err != nil {
+ t.Fatal(err)
+ }
+
+ t.Logf("cipher-text: %v", hex.EncodeToString(iw.Bytes()))
+ t.Logf("decrypting cipher text ---")
+
+ ir := new(bytes.Buffer)
+ cr, err := rabbitio.NewWriterCipher(key, ivt, ir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if _, err := io.Copy(cr, iw); err != nil {
+ t.Fatal(err)
+ }
+
+ if ir.String() != txt {
+ t.Error("error: ir.String() is not equal to txt")
+ }
+}
+
+func TestNewReaderCipher(t *testing.T) {
+ key, ivt := []byte("12345678abcdefgh"), []byte("1234qwer")
+ txt := "test NewReadercipher text dummy tx"
+ twr := strings.NewReader(txt)
+
+ iw := new(bytes.Buffer)
+
+ t.Logf("encrypting plain text ---")
+ cw, err := rabbitio.NewReaderCipher(key, ivt, twr)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if _, err := io.Copy(iw, cw); err != nil {
+ t.Fatal(err)
+ }
+
+ t.Logf("cipher-text: %v", hex.EncodeToString(iw.Bytes()))
+ t.Logf("decrypting cipher text ---")
+
+ ir := new(bytes.Buffer)
+ cr, err := rabbitio.NewReaderCipher(key, ivt, iw)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if _, err := io.Copy(ir, cr); err != nil {
+ t.Fatal(err)
+ }
+
+ if ir.String() != txt {
+ t.Error("error: ir.String() is not equal to txt")
+ }
+}
+
+func TestNewCipher(t *testing.T) {
+ key, ivt := []byte("12345678abcdefgh"), []byte("1234qwer")
+ txt := "test NewReadercipher text dummy tx"
+ cph, err := rabbitio.NewCipher(key, ivt)
+ if err != nil {
+ t.Fatal(err)
+ }
+ dst := make([]byte, len(txt))
+ cph.XORKeyStream(dst, []byte(txt))
+ t.Logf("cipher-text: %v", hex.EncodeToString(dst))
+
+ cph, err = rabbitio.NewCipher(key, ivt)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ nds := make([]byte, len(dst))
+ cph.XORKeyStream(nds, dst)
+ if string(nds) != txt {
+ t.Error("error: nds is not equal to txt")
+ }
+
+}
diff --git a/iowrp.go b/iowrp.go
new file mode 100644
index 0000000..212d658
--- /dev/null
+++ b/iowrp.go
@@ -0,0 +1,26 @@
+package rabbitio
+
+import (
+ "crypto/cipher"
+ "io"
+)
+
+// NewWriterCipher warp a rabbit cipher stream with an io.Writer, returned StreamWriter
+// interface witch can be used to encrypt or decrypting data
+func NewWriterCipher(key []byte, iv []byte, wr io.Writer) (*cipher.StreamWriter, error) {
+ stream, err := NewCipher(key, iv)
+ if err != nil {
+ return nil, err
+ }
+ return &cipher.StreamWriter{S: stream, W: wr}, err
+}
+
+// NewWriterCipher warp a rabbit cipher stream with an io.Reader, returned StreamReader
+// interface witch can be used to encrypt or decrypting data
+func NewReaderCipher(key []byte, iv []byte, re io.Reader) (*cipher.StreamReader, error) {
+ stream, err := NewCipher(key, iv)
+ if err != nil {
+ return nil, err
+ }
+ return &cipher.StreamReader{S: stream, R: re}, err
+}
diff --git a/rabbit.go b/rabbit.go
new file mode 100644
index 0000000..d70d835
--- /dev/null
+++ b/rabbit.go
@@ -0,0 +1,144 @@
+package rabbitio
+
+import (
+ "crypto/cipher"
+ "encoding/binary"
+ "errors"
+ "math/bits"
+)
+
+const (
+ invalidKeyLen = "rabbitio: key must be 16 byte len, not more not less"
+ invalidIVXLen = "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
+ ks []byte
+ carry uint32
+ sbit [16]byte
+}
+
+// NewCipher returns a chpher.Stream interface that implemented an XORKeyStream method
+// according to RFC 4503, key must be 16 byte len, iv on the other hand is optional but
+// 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) != 0x10 {
+ return nil, errors.New(invalidKeyLen)
+ }
+ if len(iv) != 0x00 && len(iv) != 0x08 {
+ return nil, errors.New(invalidIVXLen)
+ }
+ var k [0x04]uint32
+ for i := range k {
+ k[i] = binary.LittleEndian.Uint32(key[i*0x04:])
+ }
+ var r rabbitCipher
+ r.setupKey(k[:])
+ if len(iv) != 0x00 {
+ var v [0x04]uint16
+ for i := range v {
+ v[i] = binary.LittleEndian.Uint16(iv[i*0x02:])
+ }
+ r.setupIV(v[:])
+ }
+ return &r, nil
+}
+
+func (r *rabbitCipher) setupKey(key []uint32) {
+ r.xbit[0] = key[0]
+ r.xbit[1] = key[3]<<16 | key[2]>>16
+ r.xbit[2] = key[1]
+ r.xbit[3] = key[0]<<16 | key[3]>>16
+ r.xbit[4] = key[2]
+ 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], 0x10)
+ r.cbit[1] = key[0]&0xffff0000 | key[1]&0xffff
+ r.cbit[2] = bits.RotateLeft32(key[3], 0x10)
+ r.cbit[3] = key[1]&0xffff0000 | key[2]&0xffff
+ r.cbit[4] = bits.RotateLeft32(key[0], 0x10)
+ r.cbit[5] = key[2]&0xffff0000 | key[3]&0xffff
+ r.cbit[6] = bits.RotateLeft32(key[1], 0x10)
+ 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]
+ r.cbit[3] ^= r.xbit[7]
+ r.cbit[4] ^= r.xbit[0]
+ r.cbit[5] ^= r.xbit[1]
+ r.cbit[6] ^= r.xbit[2]
+ r.cbit[7] ^= r.xbit[3]
+}
+
+func (r *rabbitCipher) setupIV(iv []uint16) {
+ r.cbit[0] ^= uint32(iv[1])<<0x10 | uint32(iv[0])
+ r.cbit[1] ^= uint32(iv[3])<<0x10 | uint32(iv[1])
+ r.cbit[2] ^= uint32(iv[3])<<0x10 | uint32(iv[2])
+ r.cbit[3] ^= uint32(iv[2])<<0x10 | uint32(iv[0])
+ r.cbit[4] ^= uint32(iv[1])<<0x10 | uint32(iv[0])
+ r.cbit[5] ^= uint32(iv[3])<<0x10 | uint32(iv[1])
+ r.cbit[6] ^= uint32(iv[3])<<0x10 | uint32(iv[2])
+ r.cbit[7] ^= uint32(iv[2])<<0x10 | uint32(iv[0])
+ for i := 0; i < 4; i++ {
+ r.nextState()
+ }
+}
+
+func (r *rabbitCipher) nextState() {
+ var GRX [0x08]uint32
+ 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], 0x10) + bits.RotateLeft32(GRX[6], 0x10)
+ r.xbit[0x01] = GRX[1] + bits.RotateLeft32(GRX[0], 0x08) + GRX[7]
+ r.xbit[0x02] = GRX[2] + bits.RotateLeft32(GRX[1], 0x10) + bits.RotateLeft32(GRX[0], 0x10)
+ r.xbit[0x03] = GRX[3] + bits.RotateLeft32(GRX[2], 0x08) + GRX[1]
+ r.xbit[0x04] = GRX[4] + bits.RotateLeft32(GRX[3], 0x10) + bits.RotateLeft32(GRX[2], 0x10)
+ r.xbit[0x05] = GRX[5] + bits.RotateLeft32(GRX[4], 0x08) + GRX[3]
+ r.xbit[0x06] = GRX[6] + bits.RotateLeft32(GRX[5], 0x10) + bits.RotateLeft32(GRX[4], 0x10)
+ r.xbit[0x07] = GRX[7] + bits.RotateLeft32(GRX[6], 0x08) + GRX[5]
+}
+
+func (r *rabbitCipher) extract() {
+ var sw [0x04]uint32
+ r.nextState()
+ sw[0] = r.xbit[0] ^ (r.xbit[5]>>0x10 | r.xbit[3]<<0x10)
+ sw[1] = r.xbit[2] ^ (r.xbit[7]>>0x10 | r.xbit[5]<<0x10)
+ sw[2] = r.xbit[4] ^ (r.xbit[1]>>0x10 | r.xbit[7]<<0x10)
+ sw[3] = r.xbit[6] ^ (r.xbit[3]>>0x10 | r.xbit[1]<<0x10)
+ for i := range sw {
+ binary.LittleEndian.PutUint32(r.sbit[i*0x04:], sw[i])
+ }
+ r.ks = r.sbit[:]
+}
+
+// XORKeyStream read from src and perform xor on every elemnt of src and
+// write result on dst
+func (r *rabbitCipher) XORKeyStream(dst, src []byte) {
+ for i := range src {
+ if len(r.ks) == 0x00 {
+ r.extract()
+ }
+ dst[i] = src[i] ^ r.ks[0x00]
+ r.ks = r.ks[0x01:]
+ }
+}
+
+func gfunction(u, v uint32) uint32 {
+ uv := uint64(u + v)
+ uv *= uv
+ return uint32(uv>>0x20) ^ uint32(uv)
+}

Snix LLC Git Repository Holder Copyright(C) 2022 All Rights Reserved Email To Snix.IR