1

Assuming this is my encrypt and decrypt function using native crypto from node.js.

var algo = 'aes-256-cbc';
var algoSecret = 'mySecret';

var encrypt = function(secret){
    var cipher = require('crypto').createCipher(algo,algoSecret);
    var crypted = cipher.update(secret,'utf8','hex')
    crypted += cipher.final('hex');
    return crypted;
}
var decrypt = function(text){
    var decipher = require('crypto').createDecipher(algo,algoSecret);
    var dec = decipher.update(text,'hex','utf8');
    dec += decipher.final('utf8');
    return dec;
}

I have to encrypt data of length 20. However, the first 8 chars are always the same and known by everyone. Ex: Always starts with api-key=. Does including or removing the 8 first chars affect the security of the system?

Ex: encrypt('api-key=askjdhaskdhaskd') vs 'api-key=' + encrypt('askjdhaskdhaskd')

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
RainingChain
  • 7,397
  • 10
  • 36
  • 68

1 Answers1

3

Does including or removing the 8 first chars affect the security of the system?

Yes, but only slightly.

CBC mode with a static IV is deterministic, which means that an attacker who only observes ciphertexts can determine if the same plaintext prefix was sent before. Since the first 8 bytes are known to be static, the chance for the next 8 bytes of the first block are much more probable to equal another API key, which does not necessarily match in its entirety. Whether this information is useful to the attacker is a completely different question.

It would be better to always generate an unpredictable (read: random) IV for CBC mode instead of relying on the same IV that is derived from a password. An IV is always 16 bytes or 32 hex-encoded characters long for AES regardless of key size.

Some example code:

var crypto = require('crypto');
var algo = 'aes-256-cbc';
var algoSecret = 'mySecret';
var key = crypto.pbkdf2Sync(algoSecret, 'salt', 1000, 256, 'sha256'); 
// the key can also be stored in Hex in order to prevent PBKDF2 invocation

var encrypt = function(secret){
    var iv = crypto.randomBytes(16);
    var cipher = crypto.createCipheriv(algo, key, iv);
    var crypted = cipher.update(secret,'utf8','hex')
    crypted += cipher.final('hex');
    return iv.toString('hex') + crypted;
}
var decrypt = function(text){
    var iv = new Buffer(text.slice(0, 32), 'hex');
    text = text.slice(32);
    var decipher = crypto.createDecipheriv(algo, key, iv);
    var dec = decipher.update(text,'hex','utf8');
    dec += decipher.final('utf8');
    return dec;
}

This is still not enough, because this code might be vulnerable to the padding oracle attack depending on your communication architecture. You should authenticate the ciphertexts with a message authentication code (MAC). A popular choice is HMAC-SHA256 for "encrypt-then-MAC".

var crypto = require('crypto');
var algo = 'aes-256-cbc';
var algoSecret = 'mySecret';
var key = crypto.pbkdf2Sync(algoSecret, 'salt', 1000, 512, 'sha512');
var keyMac = key.slice(32);
var keyEnc = key.slice(0, 32);

var encrypt = function(secret){
    var iv = crypto.randomBytes(16);
    var cipher = crypto.createCipheriv(algo, keyEnc, iv);
    var crypted = cipher.update(secret,'utf8','hex')
    crypted += cipher.final('hex');
    var ct = iv.toString('hex') + crypted;

    var hmac = crypto.createHmac('sha256', keyMac);
    hmac.update(ct);
    return ct + hmac.digest('hex');
}
var decrypt = function(text){
    var hmac = crypto.createHmac('sha256', keyMac);
    hmac.update(text.slice(0, -64));
    if (hmac.digest('hex') !== text.slice(-64)) { // TODO: contant-time comparison
        // TODO: do some decoy decryption
        return false;
    }

    var iv = new Buffer(text.slice(0, 32), 'hex');
    text = text.slice(32, -64);
    var decipher = crypto.createDecipheriv(algo, keyEnc, iv);
    var dec = decipher.update(text,'hex','utf8');
    dec += decipher.final('utf8');
    return dec;
}
Community
  • 1
  • 1
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Note that the authentication is done over the Hex-encoded ciphertext and not over the actual ciphertext. This means easy implementation, but it's a bit unorthodox. – Artjom B. May 31 '16 at 04:58
  • Hi, If I have a simple NodeJS .js script where I'm using mssql module (http://stackoverflow.com/questions/37876906/nodejs-mssql-too-many-parameters-passed-stored-procedure) how can I use your logic above, if I want to hide/not have the password variable inside the config array. Basically, I'm trying not to expose the config.password variable which myssql is using to make connection for executing the stored procedure? I'll appreciate if you can provide some light, or post the solution to my post as I mentioned here. Thanks a lot – AKS Jun 29 '16 at 21:45