starcrypto/symm/ctr_seek.go

57 lines
1.2 KiB
Go

package symm
import (
"crypto/cipher"
"errors"
)
var (
ErrInvalidCTROffset = errors.New("ctr offset must be non-negative")
ErrCTRCounterOverflow = errors.New("ctr counter overflow")
)
func xorCTRAtOffset(block cipher.Block, data, iv []byte, offset int64) ([]byte, error) {
if offset < 0 {
return nil, ErrInvalidCTROffset
}
if len(iv) != block.BlockSize() {
return nil, errors.New("iv length must match block size")
}
counter := make([]byte, len(iv))
copy(counter, iv)
blockSize := int64(block.BlockSize())
blockOffset := uint64(offset / blockSize)
byteOffset := int(offset % blockSize)
if err := addUint64ToCounter(counter, blockOffset); err != nil {
return nil, err
}
stream := cipher.NewCTR(block, counter)
if byteOffset > 0 {
skip := make([]byte, byteOffset)
stream.XORKeyStream(skip, skip)
}
out := make([]byte, len(data))
stream.XORKeyStream(out, data)
return out, nil
}
func addUint64ToCounter(counter []byte, inc uint64) error {
if inc == 0 {
return nil
}
for i := len(counter) - 1; i >= 0 && inc > 0; i-- {
sum := uint64(counter[i]) + (inc & 0xff)
counter[i] = byte(sum)
inc = (inc >> 8) + (sum >> 8)
}
if inc > 0 {
return ErrCTRCounterOverflow
}
return nil
}