Search code examples
javascriptnode.jscryptographyaesscrypt

Why does Node's crypto module limit keys and IVs to 16 bytes?


So I've been writing a simple command-line program for encrypting and decrypting files with Node's built-in crypto module:

        if( deciphered_object.multibase_digest != null ){
            ciphered_object.multibase_digest = deciphered_object.multibase_digest;
            try{
                iv_buffer = Crypto.randomBytes(32);
                try{
                    cipher = Crypto.createCipheriv( 'aes-256-ofb', secret_keyobject, iv_buffer );
                    if( cipher != null && typeof(cipher) === 'object' ){
                        encrypted_buffer = cipher.update( deciphered_object.deciphered_data_buffer );
                        encrypted_buffer += cipher.final();
                        if( encrypted_buffer != null && Buffer.isBuffer(encrypted_buffer) === true ){
                            try{
                                ciphered_object.ciphered_data_buffer = Buffer.alloc( (1 + iv_buffer.length + encrypted_buffer.length) );
                                buffer_offset = ciphered_object.ciphered_data_buffer.writeUInt8( iv_buffer.length, 0 );
                                buffer_offset += iv_buffer.copy( ciphered_object.ciphered_data_buffer, buffer_offset );
                                buffer_offset += encrypted_buffer.copy( ciphered_object.ciphered_data_buffer, buffer_offset );
                                if( buffer_offset === ciphered_object.ciphered_data_buffer.length ){
                                    _return = [0, ciphered_object];
                                } else{
                                    _return = [-256, 'Error: "buffer_offset" is not equal to "ciphered_object.ciphered_data_buffer.length"'];
                                }
                            } catch(error){
                                _return = [-128, Utility.format('Buffer.alloc threw: %s', error)];
                            }
                        } else{
                            _return = [-64, 'Error: "ciphered_buffer" is either null or not a buffer.'];
                        }
                    } else{
                        _return = [-32, 'Error: "cipher" is either null or not an object.'];
                    }
                } catch(error){
                    _return = [-16, Utility.format('Crypto.createCipheriv threw: %s', error)];
                }
            } catch(error){
                _return = [-8, Utility.format('Crypto.randomBytes threw: %s', error)];
            }
        } else{
            _return = [-4, 'Error: "deciphered_object.multibase_digest" is either null or undefined.'];
        }

However, as I've discovered through brute-force testing, apparently, createCipheriv only accepts key and IV lengths of exactly 16 bytes; against the 32 bytes for the IV and 4096 bytes for the Scrypt-derived secret key, I initially planned on using:

        password = function_return[1];
        function_return = SaltFile.LoadSaltFile();
        if( function_return[0] === 0 ){
            try{
                scrypt_buffer = Crypto.scryptSync( password, function_return[1], 4096 );
                secret_keyobject = Crypto.createSecretKey( scrypt_buffer );
                if( secret_keyobject != null && typeof(secret_keyobject) === 'object' ){
                    _return = [0, secret_keyobject];
                } else{
                    _return = [-32, 'Error: "secret_keyobject" is either null or not an object.'];
                }
            } catch(error){
                _return = [-16,Utility.format('Crypto.scryptSync threw: %s', error)];
            }
        } else{
            _return = [function_return[0], 'SaltFile.LoadSaltFile: '+function_return[1]];
        }

Worst yet, neither of these size limits are mentioned in the documentation for createCipheriv: https://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options

So, really what I'm wondering is why: Why these arbitrary limits? Why have a crypto suite that forces the use ridiculously-weak keys that any old computer could brute force in a matter of minutes? Why aren't these caveats mentioned in the documentation? This 16-byte key limit seems so absurd to me and it makes the whole crypto module useless. Can this be circumvented by using a different algorithm or something? These limits don't make any sense....

Edit:

AES keys are 16, 24 and 32 bytes, which is 128, 192 and 256 bits respectively. The iv is always 16 bytes. That's the AES spec. (@jww)

Why are RSA keys commonly 4096 bits (512 bytes) but AES is capped at 32 bytes? Why is a bit entropy of 256 sufficient for AES but not RSA? What makes AES keys less susceptible to brute-force attacks than an RSA key?

If you want a 32-byte iv, then you need to switch to Rijndael. (@jww)

But I thought AES is Rijndael.

A brush-up on basic symmetric cryptography might help you determine why this question doesn't make much sense. (@Luke Joshua Park)

Really, not helpful; what reading on "basic symmetric cryptography" should I have done before asking this question? Obviously, I'm no expert, nor did I claim to be one, but, in what I have read about symmetric cryptography, it doesn't make sense that 128 bit key is sufficiently secure for AES but not RSA. If you want me to RTFM that's fine but where's this manual I was supposed to have read before asking this question? I was hoping somebody could explain why a key size of 16 bytes in secure despite that not making much intuitive sense.


Solution

  • Why have a crypto suite that forces the use of ridiculously weak keys that any old computer could brute-force in a matter of minutes?

    You are severly underestimating how big a number 2^128 is. If you could try one trillion keys per second it would take you 10e18 years to try all keys, give or take. The universe is less than 14e9 years old. You see the problem.

    AES keys (or more generally all keys for symmetric ciphers) are assumed to be chosen completely randomly. One key is as good as the other as long as the source of randomness is sufficiently unpredictable. Therefore there is no "clever" attack against the key. It's either exhaustive search of the keyspace, or attacking the algorithm itself (i.e. independently of any key).

    RSA is a totally different story, namely asymmetric cryptography. The key cannot be chosen completely randomly. It is the result of a series of computations that start out with two numbers that have specific properties. This computation is reversible, at least in principle. In the case of RSA it's the well-known factorization problem (given a number find all of its prime factors). The chosen numbers must be large enough to make factorization impossible in any reasonable amount of time.

    In summary, the key sizes for AES and RSA are not comparable whatsoever, because AES and RSA are very different cryptographic algorithms and the kinds of attacks on the respective keys are vastly different.