Search code examples
node.jsencryptionnode-streamsnode-cryptoopenpgp.js

Misformed armored text when loading Message from Stream Openpgp.js


I have a Job handling the execution of a decryption job in the background. This is the code for the decryption:

export default async function (
  job: Job<DecryptionJobPayload>,
  cb: DoneCallback,
) {
  console.log(
    `[${process.pid}] Attempting Decryption delegated to job with UUID:  ${job.id}`,
  );
  const {privateKey, sourcePath, outputPath} = job.data;
  const pKey = await opengpg.readPrivateKey({armoredKey: privateKey});
  const sourceStream = createReadStream(sourcePath);
  opengpg
    .readMessage({
      armoredMessage: sourceStream,
    })
    .then((M) => {
      opengpg
        .decrypt({
          message: M,
          decryptionKeys: pKey,
        })
        .then((e) => {
          e.data
            .pipe(createWriteStream(outputPath))
            .on('end', cb(null, 'SUCCESS'))
            .on('error', cb(new Error('Error Occured'), 'FAILED'));
        })
        .catch((err) => cb(err, 'FAILED'));
    })
    .catch((err) => {
      cb(err, 'FAILED READING THE MESSAGE FROM STREAM');
    });

The exception occurs at this catch block:

    .catch((err) => {
      cb(err, 'FAILED READING THE MESSAGE FROM STREAM');
    });

With the following Exception:

Misformed armored text
at /usr/src/app/node_modules/openpgp/src/encoding/armor.js:250:25
at processTicksAndRejections (internal/process/task_queues.js:93:5)

After Inspecting the openpgp.js code to see what causes the elevation of such exception in the unarmor function. I found these 3 cases:

  1. It reads an undefined Line:
if (line === undefined) {
   throw new Error('Misformed armored text');
              } 
  1. If the writer is done before it's actually closed:
   const { done, value } = await reader.read();
   if (done) {
     throw new Error('Misformed armored text');}
  1. I'm not really Sure... But here is the code if someone can help!
const { done, value } = await reader.read();
const reSplit = /^-----[^-]+-----$/m;
const line = value + '';
 if (line.indexOf('=') === -1 && line.indexOf('-') === -1) {
   await writer.write(line);
 } else {
   let remainder = await reader.readToEnd();
   if (!remainder.length) remainder = '';
   remainder = line + remainder;
   remainder = util.removeTrailingSpaces(remainder.replace(/\r/g, ''));
   const parts = remainder.split(reSplit);
   if (parts.length === 1) {
     throw new Error('Misformed armored text');
   }

At first i thought the issue could be from the file format, but the file was generated by the same package at a previous job...

Update 1 I tested the 3rd possible cause on my file to figure out what it does. That last bit of code starts executing when we reach the part of the message where there is a "=" or a "-", For example on this test file i used :

-----BEGIN PGP MESSAGE-----

wV4D657zkXTrd+wSAQdA2p3p2iMnFX7oHSPSFSJP20J4ibR3ilwz1T6XO7jo
.....
.....
zef62S0eNiLhU1KTThfYNKLuiG8y4N90MSlH92ruYC8RFiMvuaDud52sl4jX
D/IuUi0yPSdwbuc=
=j9F8
-----END PGP MESSAGE-----

when the Reader reaches the last 3 lines it reads till the ends clears spaces and split by the regex to find the parts=["D/IuUi0yPSdwbuc==j9F8",""]. and the check passes (parts.length!==1). So I think it's safe to say this isn't the cause of the Issue!


Solution

  • I found the solution. Thanks to Daniel Huigens from the openpgp.js team! It wasn't specified in the docs but, the way to solve this is to read the data utf8 encoded, not binary. So the solution was:

    const sourceStream = createReadStream(sourcePath,'utf8')