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 }