Skip to content

Commit ed98478

Browse files
committed
crypto/cipher: ctr-aes support offset
This feature allow ctr-aes support parallel XORKeyStream
1 parent 06264b7 commit ed98478

File tree

3 files changed

+125
-8
lines changed

3 files changed

+125
-8
lines changed

src/crypto/cipher/ctr.go

+55-8
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ import (
1919
)
2020

2121
type ctr struct {
22-
b Block
23-
ctr []byte
24-
out []byte
25-
outUsed int
22+
b Block
23+
ctr []byte
24+
out []byte
25+
outUsed int
26+
offsetPos int
2627
}
2728

2829
const streamBufferSize = 512
@@ -36,7 +37,12 @@ type ctrAble interface {
3637

3738
// NewCTR returns a Stream which encrypts/decrypts using the given Block in
3839
// counter mode. The length of iv must be the same as the Block's block size.
40+
3941
func NewCTR(block Block, iv []byte) Stream {
42+
return NewCTRWithOffset(block, iv, 0)
43+
}
44+
45+
func NewCTRWithOffset(block Block, iv []byte, offsetPos int) Stream {
4046
if ctr, ok := block.(ctrAble); ok {
4147
return ctr.NewCTR(iv)
4248
}
@@ -48,13 +54,48 @@ func NewCTR(block Block, iv []byte) Stream {
4854
bufSize = block.BlockSize()
4955
}
5056
return &ctr{
51-
b: block,
52-
ctr: bytes.Clone(iv),
53-
out: make([]byte, 0, bufSize),
54-
outUsed: 0,
57+
offsetPos: offsetPos,
58+
b: block,
59+
ctr: IncreaseCtr(offsetPos, block.BlockSize(), iv),
60+
out: make([]byte, 0, bufSize),
61+
outUsed: 0,
5562
}
5663
}
5764

65+
func IncreaseCtr(offsetPos, BlockSize int, iv []byte) []byte {
66+
iv = bytes.Clone(iv)
67+
if offsetPos <= 0 {
68+
return iv
69+
}
70+
needAdd := offsetPos / BlockSize
71+
var addItems []byte
72+
73+
for {
74+
currentNum := needAdd & 0xff
75+
addItems = append(addItems, byte(currentNum))
76+
needAdd >>= 8
77+
if needAdd <= 0 {
78+
break
79+
}
80+
}
81+
for index, add := range addItems {
82+
tmpIv := iv[:len(iv)-index]
83+
84+
for i := len(tmpIv) - 1; i >= 0; i-- {
85+
if i == len(tmpIv)-1 && int(tmpIv[i])+int(add) > 255 {
86+
tmpIv[i] = byte((int(tmpIv[i]) + int(add)) % 256)
87+
add = 1
88+
continue
89+
}
90+
tmpIv[i] += add
91+
if tmpIv[i] != 0 {
92+
break
93+
}
94+
}
95+
}
96+
return iv
97+
}
98+
5899
func (x *ctr) refill() {
59100
remain := len(x.out) - x.outUsed
60101
copy(x.out, x.out[x.outUsed:])
@@ -74,6 +115,12 @@ func (x *ctr) refill() {
74115
}
75116
x.out = x.out[:remain]
76117
x.outUsed = 0
118+
119+
if x.offsetPos > 0 {
120+
offset := x.offsetPos % x.b.BlockSize()
121+
x.out = x.out[offset:]
122+
x.offsetPos = 0
123+
}
77124
}
78125

79126
func (x *ctr) XORKeyStream(dst, src []byte) {

src/crypto/cipher/ctr_aes_test.go

+37
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,40 @@ func TestCTR_AES(t *testing.T) {
100100
}
101101
}
102102
}
103+
104+
func TestCTRWithOffset_AES(t *testing.T) {
105+
for _, tt := range ctrAESTests {
106+
test := tt.name
107+
108+
c, err := aes.NewCipher(tt.key)
109+
if err != nil {
110+
t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
111+
continue
112+
}
113+
114+
for j := 0; j <= 5; j += 5 {
115+
in := tt.in[j : len(tt.in)-j]
116+
ctr := cipher.NewCTRWithOffset(c, tt.iv, j)
117+
encrypted := make([]byte, len(in))
118+
ctr.XORKeyStream(encrypted, in)
119+
if out := tt.out[j : len(in)+j]; !bytes.Equal(out, encrypted) {
120+
t.Errorf("%s/%d: CTR\ninpt %x\nhave %x\nwant %x", test, len(in), in, encrypted, out)
121+
}
122+
}
123+
124+
for j := 0; j <= 7; j += 7 {
125+
in := tt.out[j : len(tt.out)-j]
126+
ctr := cipher.NewCTRWithOffset(c, tt.iv, j)
127+
plain := make([]byte, len(in))
128+
ctr.XORKeyStream(plain, in)
129+
if out := tt.in[j : len(in)+j]; !bytes.Equal(out, plain) {
130+
t.Errorf("%s/%d: CTRReader\nhave %x\nwant %x", test, len(out), plain, out)
131+
}
132+
}
133+
134+
if t.Failed() {
135+
break
136+
}
137+
}
138+
139+
}

src/crypto/cipher/ctr_test.go

+33
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,36 @@ func TestCTR(t *testing.T) {
5353
}
5454
}
5555
}
56+
57+
58+
var ctrAESIncreaseTests = []struct {
59+
iv []byte
60+
offset int
61+
ivWant []byte
62+
}{
63+
{
64+
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
65+
100*16,
66+
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100},
67+
},
68+
{
69+
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
70+
(100*256+100)*16,
71+
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 100},
72+
},
73+
{
74+
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
75+
(100*256*256*256)*16,
76+
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0},
77+
},
78+
}
79+
80+
81+
func TestIncreaseCtr(t *testing.T) {
82+
for _, item :=range ctrAESIncreaseTests{
83+
ivNext:=cipher.IncreaseCtr(item.offset, 16, item.iv)
84+
if !bytes.Equal(ivNext, item.ivWant) {
85+
t.Errorf("for iv %d\noffset %x\nwant %x", item.iv, item.offset, item.ivWant)
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)