Sanarothe
Sanarothe

Reputation: 302

Why does my decipher.final fail given an incorrect key?

Problem and App BG: So I'm creating a nodeJS application which stores some sensitive data, and I would like to encrypt everything. But when I try to decrypt some data with the wrong key, I get a TypeError: DecipherFinal fail error.

Here is how my application's security works: (Note that this is for AppJS and NOT a web-facing application)

  1. User can 'create account', which creates a new file; The first line of the file serves as an identifier: encrypt("Somestring" + username)

  2. User can login, which opens up the file and decrypts the first line using their password. If it happens to be "Somestring" + username, then we log the user in.

This is working just fine as long as I don't use the wrong key, which results in the DecipherFinal fail error.

Questions:

  1. Is it even possible to do what I'm trying to do? I assumed that if I ran the data through the decryption function with an incorrect key, I would just get incorrect data.

  2. Is this a bad design? I read somewhere that you shouldn't store a hashed password in the same database as the data encrypted with that password, so instead of storing a password at all I figured the user could just keep track of it, and they could decrypt an expected message for authorization. The nature of the data is such that it's VERY important that nobody else gets their hands on it, but NOT VERY important that the user can recover their data.

  3. Is this "rolling my own crypto"? Does that mean sticking to known patterns or just sticking to proven primitives and algorithms? If this is rolling my own, are there any implementations of an encrypted container I could use for Node?

Code:

https://gist.github.com/sanarothe/5993384

Thanks a lot,

Cameron

Upvotes: 0

Views: 482

Answers (1)

Joachim Isaksson
Joachim Isaksson

Reputation: 180997

As for why decryption failed, AES256 has a 16 byte block size (ie, it can only encrypt data in even 16 byte blocks) Since your data isn't necessarily a size that is divisible by 16, the crypto library has autopadding turned on to add some filler data (PKCS in this case) to make the length divisible by 16.

The padding is constructed so that when decryption gets to the end of the data, it knows exactly what bytes to remove to cut the message off at the right byte and not return junk at the end. The problem is that when it ends with random data, the "unpadding" detects that it cannot find the end marker and fails decryption. Note that totally random data may actually contain a valid marker, so you're not in any way guaranteed that this check will fail even on random data.

If you really don't want decryption to fail on a padding mismatch, you can ensure that your data's length is divisible by 16 (by padding it yourself in whatever way you like) and turn off autopadding using cipher.setautopadding(false) before calling final on both encryption and decryption. That way, you won't get decryption failures, and will, as you're trying, have to detect crypto failures yourself.

As for design, to me the whole scheme sounds a bit like rolling your own security. For example, if you add data that can be checked for validity (as you do with the first row of known data), you're helping attackers by allowing them to work with a (more or less) known plain text at a known location in the stream to test keys. AES256 is fairly secure against known plain text attacks for now, but you never know in a few years.

A second thing, you'll want to use AES-256-CBC or similar, I'm unsure what mode of operation the crypto modure uses by default.

Upvotes: 2

Related Questions