You could use ddrescue and cryptsetup:
truncate -s $(blockdev --getsize64 /dev/sdx) sdx-zero
cryptsetup open --type plain --cipher aes-xts-plain64 sdx-zero sdx-random
ddrescue /dev/mapper/sdx-random /dev/sdx sdx-scrub.map
To resume, preserve sdx-scrub.map and then just repeat the same commands again.
If you use the same passphrase every time, this method also allows verification:
cmp /dev/mapper/sdx-random /dev/sdx && echo OK || echo FAIL
However, to make this resumable, you'd have to use cmp -i SKIP -n LIMIT.
With verification, the process will take twice as long. Without verification, you have a Schroedinger's scrub.
In the above example, /dev/sdx is the drive to be scrubbed.
sdx-zero is a sparse file containing only zeroes, same size as /dev/sdx. It must be backed by a filesystem that supports sparse files properly, ext4/xfs/btrfs works, tmpfs/fat/ntfs does not.
cryptsetup encrypts zeroes to random data, so /dev/mapper/sdx-random is a seekable block device full of random data (unlike /dev/urandom which is not seekable).
ddrescue reads random data from sdx-random and writes it to /dev/sdx, thereby scrubbing it while tracking progress in sdx-scrub.map. It will also show you a progress bar as well as errors if any.
If you prefer not typing a passphrase every time, you can also create a keyfile instead:
printf "%s" $(uuidgen) > sdx-scrub.key
Generate the keyfile only once and preserve it between calls, then just add --key-file sdx-scrub.key to the cryptsetup command.
See also https://unix.stackexchange.com/a/352378/30851