1

I wrote an application in Nodejs that encrypts user passwords using AES-256-CTR :

const crypto = require('crypto')
const masterkey = 'azertyuiopazertyuiopazertyuiopaz'
const cipher = crypto.createCipher('aes-256-ctr', masterkey)
console.log(cipher.update('antoine', 'utf8', 'hex') + cipher.final('hex')) //=> 6415bc70ad76c6

It then gets persisted into a database and now I'm trying to decipher it from a Python script using PyCrypto like this :

masterkey = 'azertyuiopazertyuiopazertyuiopaz'
password = '6415bc70ad76c6'

from Crypto.Cipher import AES
import os
import binascii
counter = os.urandom(16)
# counter = bytes(16) # does not work
# counter = masterkey[0:16].encode() # does not work
cipher = AES.new(masterkey, AES.MODE_CTR, counter=lambda: counter)
print(cipher.decrypt(binascii.a2b_hex(password)))

But it gives me completely wrong results here.

Do you know what I am missing ?

EDIT

Thanks to zaph, it appears that the way my Javascript code encrypts data is insecure. I still have to figure out what IV is being used internally by Node. I've tried many without success

masterkey[0:16].encode()
bytes(16)
Blackrush
  • 117
  • 9
  • This appears to be a duplicate of [PyCrypto problem using AES+CTR](https://stackoverflow.com/questions/3154998/pycrypto-problem-using-aesctr) – cowbert Jul 31 '17 at 22:55
  • 1
    Provided example sample data and the encrypted data (in hex) along with the key. Add that to the question. – zaph Jul 31 '17 at 22:55
  • cowbert, from what zaph said and various documentation around, it appears the root of the problem come from the counter variable which must be computed somehow in accordance with Node's behavior. – Blackrush Jul 31 '17 at 23:06

1 Answers1

3

Update based on new information in the question: The best bet is that Nodejs is using a default counter value.

The same counter value must be used for both encryption and decryption. But no counter value is provided on encryption and a random value is used on decryption so it can never work.

Use: crypto.createCipheriv(algorithm, key, iv) where iv is the random counter initial value.

It is necessary to create a random counter value on encryption and save it so that the same initial counter value can be used on decryption. One option is to prefix the encrypted data with the counter value, it does not need to be secret. Then on decryption it can be split from the encrypted data and used.

Also when using CTR mode the same initial counter value must never be use again with the same key.

See CTR mode

PyCrypto documentation CTR mode:

MODE_CBC
Cipher-Block Chaining (CBC). Each of the ciphertext blocks depends on the current and all previous plaintext blocks. An Initialization Vector (IV) is required.

The IV is a data block to be transmitted to the receiver. The IV can be made public, but it must be authenticated by the receiver and it should be picked randomly.)

The IV is the initial counter value.

[Nodejs dociumewnrtation: Class: Cipher:

crypto.createCipheriv(algorithm, key, iv)
    algorithm <string>  
    key <string> | <Buffer> | <TypedArray> | <DataView>
    iv <string> | <Buffer> | <TypedArray> | <DataView>

Creates and returns a Cipher object, with the given algorithm, key and initialization vector (iv).

zaph
  • 111,848
  • 21
  • 189
  • 228
  • What do you recommend in this case then ? It can be decrypted flawlessly from Node. – Blackrush Jul 31 '17 at 22:48
  • 1
    because node is caching its counter value somehow (or being lazy and using the same counter initial value for every encryption (likely using the masterkey hex as a the counter IV)). You're going to have to figure out what counter IV the node module uses when it encrypts in CTR mode. – cowbert Jul 31 '17 at 22:53
  • Seems unlikely it gets cached as I am able to decrypt the passwords across multiple run of the same Javascript script. – Blackrush Jul 31 '17 at 23:04
  • It probably defaults to 0 which is not secure. As an experment try this: encrypt the same data twice, the result should be different if a random counter (IV) is being used. If not it is not secure. – zaph Jul 31 '17 at 23:07
  • You are right, the result every time is the same. It's really startling to see this. – Blackrush Jul 31 '17 at 23:10
  • Moreover, I tried to set the counter variable to "masterkey[0:16].encode()" And "bytes(16)" without success – Blackrush Jul 31 '17 at 23:10
  • Alright thanks zaph for you help! i'm really curious what Node does behind these snippet. I guess right now by biggest concern will be to reimplement my ciphering code from my Javascript application. It's also very concerning that this kind of JS code can be found in Node's own official documentation. – Blackrush Jul 31 '17 at 23:18
  • Very usually encryption is done with CBC mode and with a random IV, it can be prepended to the encrypted data, and PKCS padding to handle data that is not a multiple of the block size. CTR mode done correctly is fine but the counter nonce (IV) muse be unique for each encryption with the same key. – zaph Jul 31 '17 at 23:25
  • You're right zaph, `crypto.createCipheriv` seems to fix it. Thanks! – Blackrush Aug 01 '17 at 09:06