TomFrost/Cryptex

Windows: decrypt on command line throwing Error: Invalid IV length

Closed this issue · 7 comments

Hi, I've setup Cryptex in my Node App which I've written on Windows.

I tried to deploy on AWS but I got this error at runtime: InvalidCiphertextException: null

So I went back to Windows and tried to decrypt, but hit the error below.

So I regenerated my AES256 key, and re-encrypted, giving the output you can see in my config.json below. Here are the steps I took to re encrypt:

$ cryptex -e prod -f user-config\accounts\global-cryptex-config.json encrypt
D931nf5zySSbLh6UoISLioyeESsDpLW5eyI6giN8fMZSrRHUaLIZq6vThesOP6CPm61IPbcqJVdctzLOj2BD9g==

  • Saved output under 'main_read' in config.json 'secrets'

$ cryptex -e prod -f user-config\accounts\global-cryptex-config.json decrypt main_read
Uh-oh, we have a problem:
Error: Invalid IV length
at new Decipheriv (crypto.js:267:16)
at Object.Decipheriv (crypto.js:264:12)
at AES256.decrypt (C:\Users\joshr\AppData\Roaming\npm\node_modules\cryptex\src\lib\SymmetricAlgo.js:22:29)
at Promise.resolve.then.then (C:\Users\joshr\AppData\Roaming\npm\node_modules\cryptex\src\index.js:51:30)
at
at process._tickDomainCallback (internal/process/next_tick.js:228:7)


global-cryptex-config.json

{
  "prod": {
    "keySource": "kms",
    "keySourceOpts": {
      "region": "us-east-2",
      "dataKey": "AQIDAHifIsA1ShHsBErvy/UgTCtakh3i5N0iK2Sf6VctfT/MVwG3qqPHpoMBHY2NKd4nfv00AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMSShsiqEW5NA1cxOFAgEQgDtdNNS/usuuQLVga0OzGVNBtq2TFDzamR0Tw4lrFkEYKcQ19QZdan0diM8MG6L+UJ/sc7S2Qb+8I3ViDg=="
    },
    "secrets": {
    "main_read": "KE9lH7c1DF9FOZMQFBCSrJoYpMDHvNN2vG1hs+wokBxAQkVU78qDwte80Km7+UMWcmGXdRBDmM64rO06Crxpng=="
    }
  }
}

I also tried encrypting with "s around SECRET to see if there was an issue with CMD, but that made no difference.

I've realised my mistake now, which originated from reading this article:
http://technologyadvice.github.io/lock-up-your-customer-accounts-give-away-the-key/

Once your cryptex.json has some named secrets in it, decrypting them is as simple as:

cryptex decrypt mysqlpass
# Output: SomeSecretPassword

This doesn't actually work in my case, I'd need to fetch the value stored under the key "mysqlpass" in secrets and then run that in it's place in the decrypt command, e.g.

cryptex decrypt 14h/K43TNhsp/slpikmlGj3zMOnCdPJLUe5AphTA3k7PW169m05K1vt880IXd6bL
# Output: SomeSecretPassword

And whether it's the time that was given for AWS to assign the role or the regeneration of the API keys, my system is now working in the cloud.

I have noticed that the NodeJS API cryptex.getSecret() takes the key value from the secrets object, but decrypt on the command line takes the actual encrypted string. While this makes sense now, that article was misleading and there wasn't much in the cryptex docs that I could see to resolve the confusion.

But cheers for the tasty software! It's definitely solved my problem as a lone wolf dev that needs some security :) 👍

Hey Josh! I’m so sorry, there was a terrible typo in that article that I can’t repair because I no longer work for the company in question. But hopefully this can ease the disparity a bit:

On the command line AND in the Cryptex API, “decrypt” takes a base64 encrypted string, and “getSecret” takes a named secret from your config. Using “getSecret” instead of “decrypt” on the CLI would have solved your initial issue.

Apologies for the eternally incorrect blog post!

Also worth note: shortly after my release of Cryptex, Amazon released Parameter Store which does something very similar, but managed in the console. Today, they released another secret storage solution at the AWS summit in San Fran that oddly does basically the exact same thing as Parameter Store, but apparently just marketed better to this use case. Either way, I still use Cryptex for incredibly easy access to secure local encryption/decryption, but if your need is purely secret management, those could be good options too.

All the best!

Hey Josh, apologies for the delayed reply! Your assessment was very close-- let me go point by point:

  1. KMS stores a master key that can only be accessed via the root AWS account or with valid permissions.

Indeedy! KMS is essentially a shared hardware security module (HSM). HSMs store their keys inside of them and there is no way to get the key out. All you can do is feed in an input and instruct the HSM to encrypt or decrypt. If you need ultra-privacy, AWS provides dedicated HSMs that are quite a bit more costly.

  1. To indirectly use that key (and therefore that identity) I created a "datakey" via the AWS CLI after proving I have those permissions.

Yep, your "datakey" is a plain old AES symmetric encryption key that itself has been encrypted by KMS.

  1. Cryptex used that datakey and my aws permissions to access the master key to:
    --> a. Encrypt the secret using another key, E
    --> b. Append key E to the encrypted string.
    --> c. Encrypt all of that using my master AWS key. This is the output of the encrypt function.

Here's where your explanation falls apart. When you want to encrypt data (like a secret, but also anything else), Cryptex sends its encrypted AES key -- the datakey -- into KMS, and gets the decrypted AES key back. Neither Cryptex nor AWS ever ever stores this decrypted key anywhere. It uses it to encrypt your secret, and then boom, the decrypted key is wiped from memory. This happens every time you encrypt something.

I'm guessing from reading your explanation above that you dove into the unadvertised behind-the-scenes of Cryptex's encryption process. Cryptex generates a cryptographically secure random string (fancy words for "random in a way that is effectively impossible to predict") called an initialization vector, or "iv" that it seeds the encryption algorithm with. This is what makes sure you get two different strings back when you encrypt the same string twice -- otherwise it would be obvious even from looking at your encrypted secrets when you've used the same secret more than once. But you need to know what IV you used in order to decrypt the string later, so the encrypted secret you get back is actually the IV concatenated with the encrypted secret. That's why your encrypted secret is noticeably longer than the original string. Note that the IV needn't be kept private; it's not secret and it doesn't provide any security beyond your AES key. It's there solely to make sure there's no pattern to your secrets.

... And when decrypting
--> 4. Cryptex uses the AWS SDK(?) to verify that permissions to use the
master key are available.
--> 5. Fetches the master key and decrypts the envelope, exposing key E.
--> 6. Decrypts the secret using key E.

Not exactly, but you can probably guess what happens if you followed my explanation for the last step. Cryptex does the same thing with the datakey for decryption that it does for encryption. It passes it into KMS to get the decrypted AES key (which obviously fails if you don't have permission to use the KMS key that the datakey was encrypted with). Once it has the decrypted KMS key, it can use that to decrypt any/all of your secrets without any additional calls to KMS. So it doesn't matter if you have one secret or 900, once you have that decrypted AES key, all decryption happens locally and is breakneck fast.

Let me know if any part of the process is still cloudy-- I'm happy to expand! Big props for getting into security. It's absolutely the most overlooked field in software engineering and it drives me nuts. Especially because it's so much fun!