void encrypt(void *dst, const void *src, u64 len, const void *keys, const void *ofs);
void decrypt(void *dst, const void *src, u64 len, const void *keys, const void *ofs);
This is the same problem as Challenge 2,
except that the length argument doesn't have to be a multiple of your
blocksize. You are not allowed to use padding or counter mode. And
you have to any number of bytes as the length.
For security, all candidates are considered barely secure until somebody finds an attack faster than brute-forcing a 256bit key.
It makes sense to continue using a normal blocksize for everything except the tail. If your blocksize is 16 bytes, you may have up to 15 remaining bytes to deal with. For those you need a different solution.
You can create a block cypher with a block size of 1 byte. For example, you could repeatedly apply the AES s-box, then XOR with a key byte. Given enough rounds, this should be secure. It would also be rather slow.