rzcoder / node-rsa

Node.js RSA library
1.39k stars 205 forks source link

Running under node, io.js does not properly pass OAEP hash and label parameters to crypto.publicEncrypt() #229

Open tonesandtones opened 10 months ago

tonesandtones commented 10 months ago

The problem

When running in a Node process, encrypting with PKCS1 OAEP padding with custom label and SHA 256 hash algorithm, io.js does not pass the OAEP hash and OAEP label parameters to crypto.publicEncrypt().

Expected behaviour

When run in a node process, OAEP padding is applied with the chosen hash function and label before being encrypted.

Actual behaviour

OAEP padding is applied with node crypto defaults of SHA1 and no label instead of the requested hash and label. See https://nodejs.org/api/crypto.html#cryptopublicencryptkey-buffer

image

The cause

When running in a node process, node-rsa selects an encrypt engine wrapper encryptEngines/io.js or encryptEngines/node12.js. Neither passes the configured oaep hash or label to crypto.publicEncrypt().

Workaround

When specifying your NodeRSA options object, set environment: 'browser' to cause node-rsa to select the encrypt engine encryptEngines/js.js, which implements is own oaep padding.

Example workaround

//MUST set environment: 'browser'. When run with environment == 'node' (or autodetected
//to be running in a node process), node-rsa doesn't properly pass the oaep hash and
//label parameters to node crypto and defaults to SHA-1 and no label.
//Using 'browser' causes node-rsa to use its own OAEP implementation and uses the hash
//and label parameters correctly.
const options = {
    environment: 'browser', //CRIITICAL - MUST BE 'browser'
    encryptionScheme:{
        hash: 'sha256', //message and mgf1 hash function
        label: '<OAEP padding label>', //oaep padding label, a.k.a. encoding parameter
        padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
    }
};

const key = new NodeRSA(undefined, options); //set the current key to undefined so it doesn't try to create a new public/private keypair

const rsaMod = '<long modulus component>';
const rsaExp = '<short exponent components>';

//now import the public component of our key
key.importKey({
    n: Buffer.from(rsaMod, 'hex'),
    e: Buffer.from(rsaExp, 'hex')
}, 'components-public');

const encryptedBuffer = key.encrypt(Buffer.from('<message to encrypt>'), 'buffer');
tonesandtones commented 10 months ago

encryptEngines/io.js#L35-L36 should pass the OAEP label and hash function to crypto.publicEncrypt() here.

return crypto.publicEncrypt({
        key: options.rsaUtils.exportKey('public'),
        padding: padding
        // <-- oaepLabel: options.encryptionSchemeOptions.label
        // <-- oaepHash: options.encryptionSchemeOptions.hash
}, data);