Search code examples
goencryptionaes

Why doesn't Go's XTS package handle arbitrary data length?


I'm trying to encrypt a file with AES-256 using XTS mode & Golang. I just moved from CTR mode to XTS recently and I'm facing this error

A nice-to-have, my code snippet:

func Encrypt(loc string, k *kyber.Kyber) (esK string, err error) {
    in, err := os.Open(loc)
    if err != nil {
        return "", err
    }

    defer in.Close()

    out, err := os.Create(loc + EncryptExtension)
    if err != nil {
        return "", err
    }

    defer out.Close()

    sK, esK, err := k.SecretKey()
    if err != nil {
        return "", err
    }

    cipher, err := xts.NewCipher(aes.NewCipher, sK) // sK = 32 bytes
    if err != nil {
        return "", err
    }

    pT := make([]byte, BufferSize) // BufferSize = 2 * 1024 * 1024 = 2MB

    for {
        len, err := in.Read(pT)
        if err != nil && err != io.EOF {
            return "", err
        }

        if len == 0 {
            break
        }

        cT := make([]byte, len)
        
        cipher.Encrypt(cT, pT[:len], 0) // When len % 16 != 0 the code just panic

        if _, err := out.Write(cT); err != nil {
            return "", err
        }
    }

    return esK, nil
}

I know what the error means, and I know a workaround to fix it, but the main question here is, isn't XTS designed to handle arbitrary data length? Why doesn't Golang follow that?

  1. XTS mode

Solution

  • According to NIST 800-38E

    The XTS-AES mode was designed for the cryptographic protection of data on storage devices that use of fixed length “data units”.

    The x/crypto/xts library explicitly states the following (link):

    This package does not implement ciphertext-stealing so sectors must be a multiple of 16 bytes.

    The original CL that contributed the initial code from over 10 years ago never included support for ciphertext stealing, and it did not provide the reason why. It was likely omitted due to complexity.