Skip to content

encrypt + encode encrypted/plain emails #1058

Description

@tomholub

part of #1051

Right now we use node for initial structure and then we tag on files in Kotlin. Can do all in Kotlin.

Current implementation is at https://github.com/FlowCrypt/flowcrypt-mobile-core/blob/master/source/mobile-interface/endpoints.ts

Here:

publiccomposeEmail=async(uncheckedReq: any): Promise<Buffers>=>{constreq=ValidateInput.composeEmail(uncheckedReq);constmimeHeaders: RichHeaders={to: req.to,from: req.from,subject: req.subject,cc: req.cc,bcc: req.bcc};if(req.replyToMimeMsg){constpreviousMsg=awaitMime.decode(Buf.fromUtfStr((req.replyToMimeMsg.substr(0,10000).split('\n\n')[0]||'')+`\n\nno content`));constreplyHeaders=Mime.replyHeaders(previousMsg);mimeHeaders['in-reply-to']=replyHeaders['in-reply-to'];mimeHeaders['references']=replyHeaders['references'];}if(req.format==='plain'){constatts=(req.atts||[]).map(({ name, type, base64 })=>newAtt({ name, type,data: Buf.fromBase64Str(base64)}));returnfmtRes({},Buf.fromUtfStr(awaitMime.encode({'text/plain': req.text},mimeHeaders,atts)));}elseif(req.format==='encrypt-inline'){constencryptedAtts: Att[]=[];for(constattofreq.atts||[]){constencryptedAtt=awaitPgpMsg.encrypt({pubkeys: req.pubKeys,data: Buf.fromBase64Str(att.base64),filename: att.name,armor: false})asOpenPGP.EncryptBinaryResult;encryptedAtts.push(newAtt({name: att.name,type: 'application/pgp-encrypted',data: encryptedAtt.message.packets.write()}))}constencrypted=awaitPgpMsg.encrypt({pubkeys: req.pubKeys,data: Buf.fromUtfStr(req.text),armor: true})asOpenPGP.EncryptArmorResult;returnfmtRes({},Buf.fromUtfStr(awaitMime.encode({'text/plain': encrypted.data},mimeHeaders,encryptedAtts)));}else{thrownewError(`Unknown format: ${req.format}`);}}

Tests at https://github.com/FlowCrypt/flowcrypt-mobile-core/blob/master/source/test.ts

ava.default('composeEmail format:plain -> parseDecryptMsg',asynct=>{constcontent='hello\nwrld';const{ keys }=getKeypairs('rsa1');constreq={format: 'plain',text: content,to: ['some@to.com'],cc: ['some@cc.com'],bcc: [],from: 'some@from.com',subject: 'a subj'};const{data: plainMimeMsg,json: composeEmailJson}=awaitrequest('composeEmail',req,[]);expectEmptyJson(composeEmailJson);constplainMimeStr=plainMimeMsg.toString();expect(plainMimeStr).contains('To: some@to.com');expect(plainMimeStr).contains('From: some@from.com');expect(plainMimeStr).contains('Subject: a subj');expect(plainMimeStr).contains('Cc: some@cc.com');expect(plainMimeStr).contains('Date: ');expect(plainMimeStr).contains('MIME-Version: 1.0');const{data: blocks,json: parseJson}=awaitrequest('parseDecryptMsg',{ keys,isEmail: true},plainMimeMsg);expect(parseJson).to.deep.equal({text: content,replyType: 'plain',subject: 'a subj'});expectData(blocks,'msgBlocks',[{rendered: true,frameColor: 'plain',htmlContent: content.replace(/\n/g,'<br />')}]);t.pass();});ava.default('composeEmail format:plain (reply)',asynct=>{constreplyToMimeMsg=`Content-Type: multipart/mixed; boundary="----sinikael-?=_1-15535259519270.930031460416217"To: some@to.comFrom: some@from.comSubject: Re: originalDate: Mon, 25 Mar 2019 14:59:11 +0000Message-Id: <originalmsg@from.com>MIME-Version: 1.0------sinikael-?=_1-15535259519270.930031460416217Content-Type: text/plainContent-Transfer-Encoding: quoted-printableorig message------sinikael-?=_1-15535259519270.930031460416217--`constreq={format: 'plain',text: 'replying',to: ['some@to.com'],cc: [],bcc: [],from: 'some@from.com',subject: 'Re: original', replyToMimeMsg };const{data: mimeMsgReply, json }=awaitrequest('composeEmail',req,[]);expectEmptyJson(json);constmimeMsgReplyStr=mimeMsgReply.toString();expect(mimeMsgReplyStr).contains('In-Reply-To: <originalmsg@from.com>');expect(mimeMsgReplyStr).contains('References: <originalmsg@from.com>');t.pass();});ava.default('composeEmail format:encrypt-inline -> parseDecryptMsg',asynct=>{constcontent='hello\nwrld';const{ pubKeys, keys }=getKeypairs('rsa1');constreq={ pubKeys,format: 'encrypt-inline',text: content,to: ['encrypted@to.com'],cc: [],bcc: [],from: 'encr@from.com',subject: 'encr subj'};const{data: encryptedMimeMsg,json: encryptJson}=awaitrequest('composeEmail',req,[]);expectEmptyJson(encryptJson);constencryptedMimeStr=encryptedMimeMsg.toString();expect(encryptedMimeStr).contains('To: encrypted@to.com');expect(encryptedMimeStr).contains('MIME-Version: 1.0');expectData(encryptedMimeMsg,'armoredMsg');// armored msg block should be contained in the mime messageconst{data: blocks,json: decryptJson}=awaitrequest('parseDecryptMsg',{ keys,isEmail: true},encryptedMimeMsg);expect(decryptJson).deep.equal({text: content,replyType: 'encrypted',subject: 'encr subj'});expectData(blocks,'msgBlocks',[{rendered: true,frameColor: 'green',htmlContent: content.replace(/\n/g,'<br />')}]);t.pass();});

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions