Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AesGcm as as drop-in for OpenSSL: encryption by chunks #497

Closed
tgross35 opened this issue Jan 4, 2023 · 3 comments
Closed

AesGcm as as drop-in for OpenSSL: encryption by chunks #497

tgross35 opened this issue Jan 4, 2023 · 3 comments

Comments

@tgross35
Copy link

tgross35 commented Jan 4, 2023

Is it possible to "encrypt by chunks" in the way that you can for openssl?

I am trying to do a drop in replacement for something like this example, where unencrypted input data that comes in blocks is used to incrementally update the output. this is a GCM-specific example but the below is a good enough example for something similar:

 int do_crypt(FILE *in, FILE *out, int do_encrypt)
        {
        /* Allow enough space in output buffer for additional block */
        unsigned char inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
        int inlen, outlen;
        EVP_CIPHER_CTX ctx;
        // example key & iv
        unsigned char key[] = "0123456789abcdeF";
        unsigned char iv[] = "1234567887654321";

        /* Don't set key or IV right away; we want to check lengths */
        EVP_CIPHER_CTX_init(&ctx);
        EVP_CipherInit_ex(&ctx, EVP_aes_128_cbc(), NULL, NULL, NULL,
                do_encrypt);
        // assertions ...

        /* Now we can set key and IV */
        EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, do_encrypt);

        for(;;) 
                {
                // Load in chunks
                inlen = fread(inbuf, 1, 1024, in);
                if(inlen <= 0) break;
                if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen)) // ... run update, handle error

                fwrite(outbuf, 1, outlen, out);
                }
        if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen)) // ... unsure what this does
        fwrite(outbuf, 1, outlen, out);

        EVP_CIPHER_CTX_cleanup(&ctx);
        return 1;
        }

It seems like as far as mappings go: EVP_CIPHER_CTX_init and EVP_CipherInit_ex are both wrapped into Aes256Gcm::new(&key), and what is called iv in openssl is nonce in RustCrypto. This must be provided with each encrypt/decrypt operation, seems like openssl keeps it in the context

The closest thing to EVP_CipherUpdate looks like encrypt_in_place_detached, but this returns a tag, and I'm not sure what happens to the nonce. What is the proper way to encrypt block by block? It seems like maybe the following pseudocode would work:

// encrypt
output_buffer.write(nonce);
for block in blocks {
    let tag = ctx.encrypt_in_place_detached(nonce, input_buffer, output_buffer).unwrap();
    output_buffer.write(tag);
}

And then decryption would first read the nonce, then decrypt block by block (block size would have to be identical here). Is this the correct way to perform this task?

It seems like the correct way would mean only writing a single tag at the end, but I can't figure out how to do this.

@tarcieri
Copy link
Member

tarcieri commented Jan 4, 2023

We absolutely will not be supporting an incremental decryption API as those reveal unauthenticated decrypts of ciphertexts which break AEAD security. That is an OpenSSL design flaw. There is no safe way to do it. Any design opens you up to CCAs.

As for incremental/online encryption, that's something we won't support in aes-gcm until a proper API for it has been carefully designed in the aead crate. I thought we had an existing open issue for it (it has come up in the ccm crate, where we did reluctantly merge an API which I absolutely do not want anyone to use as "precedent" for further expanding such APIs to other crates as at this point I consider merging such changes a mistake in absence of a proper trait-based API).

We do have this open issue on the traits repo for AAD streaming specifically: RustCrypto/traits#62

I would suggest opening another one for the online encryption use case as it seems we don't currently have an open one.

That said, I'm closing this issue as we will not be adding further bespoke APIs to individual crates without a well-designed trait-based interface.

See also: the provably secure stream API the aead crate already provides for incremental encryption/decryption using authenticated segments: https://docs.rs/aead/latest/aead/stream/index.html

@tarcieri tarcieri closed this as completed Jan 4, 2023
@tgross35
Copy link
Author

tgross35 commented Jan 4, 2023

Thank you for the very thorough explanation. I'll read a bit more into this then open the issue as you recommended and take a look into how to design something that fits into my available API

@tarcieri
Copy link
Member

I opened RustCrypto/traits#1364 to track a full incremental encryption API

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants
@tarcieri @tgross35 and others