1
votes

I am not able to decrypt my messages I receive from my S3 bucket. They are encrypted with a KMS key. I use Node and Typescript.

I have tried some stuff but arrent able to make it work. Looking in to this links: https://github.com/gilt/node-s3-encryption-client/issues/3 and https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SES.html

My code look like this now:

import * as AWS from 'aws-sdk';
import * as crypto from 'crypto';    

const s3 = new AWS.S3({ apiVersion: '2006-03-01', region: 'eu-west-1' });
const kms = new AWS.KMS({ apiVersion: '2014-11-01', region: 'eu-west-1' });

export const handler = LambdaUtils.lambdaHandler( 'onebox-email-service-SendMailToL4PFunction', async (event) => {
    const record = event.Records[0];

    const request = {
      Bucket: record.s3.bucket.name,
      Key: record.s3.object.key
    };

    const data = await s3.getObject(request).promise();
    const decryptData = await decryptSES(data);

    return decryptData;
  }
);

export const decryptSES = async (objectData) => {
  const metadata = objectData.Metadata || {};
  const kmsKeyBase64 = metadata['x-amz-key-v2'];
  const iv = metadata['x-amz-iv'];
  const tagLen = (metadata['x-amz-tag-len'] || 0) / 8;
  let algo = metadata['x-amz-cek-alg'];
  const encryptionContext = JSON.parse(metadata['x-amz-matdesc']);

  switch (algo) {
    case 'AES/GCM/NoPadding':
      algo = 'aes-256-gcm';
      break;
    case 'AES/CBC/PKCS5Padding':
      algo = 'aes-256-cbc';
      break;
    default:
      log.error({Message: 'Unsupported algorithm: ' + algo});
      return;
  }

 if (typeof (kmsKeyBase64) === 'undefined') {
   log.error('Error');
 }

 const kmsKeyBuffer = new Buffer(kmsKeyBase64, 'base64');
 const returnValue = await kms.decrypt({ CiphertextBlob: kmsKeyBuffer, EncryptionContext: encryptionContext }, (err, kmsData) => {
    if (err) {
      log.error({err});
      return null;
    } else {
      const data = objectData.Body.slice(0, -tagLen);
      const decipher = crypto.createDecipheriv( algo, kmsKeys.Plaintext[0], new Buffer(iv, 'base64'));
      if (tagLen !== 0) {
        const tag = objectData.Body.slice(-tagLen);
        decipher.setAuthTag(tag);
      }
        let dec = decipher.update(data, 'binary', 'utf8');
        dec += decipher.final('utf8');
        return dec;
      }
    }).promise();

    return returnValue;
  };

I get error in my lambda that look like this:

2019-02-05T17:06:19.015Z d9593ef7-635b-47b2-b881-ede2a396f88e Error: Invalid key length at new Decipheriv (crypto.js:267:16) at Object.createDecipheriv (crypto.js:627:10) at Response.l.decrypt (/var/task/email-from-s3.js:592:232696) at Request. (/var/runtime/node_modules/aws-sdk/lib/request.js:364:18) at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:105:20) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:77:10) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:683:14) at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10) at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12) at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10 at Request. (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9) at Request. (/var/runtime/node_modules/aws-sdk/lib/request.js:685:12) at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:115:18) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:77:10) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:683:14) at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)

What I can see in my logs I get the encrypted message from my s3 bucket, but then it is not possible to decrypt it.

Can someone please help me with this? I use Node and Typescript.

1

1 Answers

1
votes

I was got some help from coworker and we could figuring it out. The problem was with the

const decipher = crypto.createDecipheriv( algo, kmsKeys.Plaintext[0], new Buffer(iv, 'base64'));

We needed to change the kms.Plaintext to kms.Plaintext as Buffer and it start working. I post my hole funktion here if someone needs it for later.

import * as AWS from 'aws-sdk';
import * as crypto from 'crypto';

const kms = new AWS.KMS({ apiVersion: '2014-11-01', region: 'eu-west-1' });

export const decryptS3Message = async (objectData) => {
  const metadata = objectData.Metadata || {};
  const kmsKeyBase64 = metadata['x-amz-key-v2'];
  const iv = metadata['x-amz-iv'];
  const tagLen = (metadata['x-amz-tag-len'] || 0) / 8;
  let algo = metadata['x-amz-cek-alg'];
  const encryptionContext = JSON.parse(metadata['x-amz-matdesc']);

  switch (algo) {
    case 'AES/GCM/NoPadding':
      algo = `aes-256-gcm`;
      break;
    case 'AES/CBC/PKCS5Padding':
      algo = `aes-256-cbc`;
      break;
    default:
      throw new ErrorUtils.NotFoundError('Unsupported algorithm: ' + algo);
  }

  if (typeof (kmsKeyBase64) === 'undefined') {
    return null;
  }

  const kmsKeyBuffer = Buffer.from(kmsKeyBase64, 'base64');

  const returnValue = await kms.decrypt({ CiphertextBlob: kmsKeyBuffer, EncryptionContext: encryptionContext }).promise()
    .then((res) => {
      const data = objectData.Body.slice(0, -tagLen);
      const decipher = crypto.createDecipheriv( algo, res.Plaintext as Buffer, Buffer.from(iv, 'base64'));
      if (tagLen !== 0) {
        const tag = objectData.Body.slice(-tagLen);
        decipher.setAuthTag(tag);
      }
      let dec = decipher.update(data, 'binary', 'utf8');
      dec += decipher.final('utf8');
      return dec;
    }).catch((err) => {
      throw new ErrorUtils.InternalServerError('Not able to decrypt message: ', err);
    });

  return returnValue;
};