Improved broken PGP implementation

This commit is contained in:
djmaze 2021-05-19 15:26:10 +02:00
parent 45c74370d0
commit e49405cd85
7 changed files with 101 additions and 230 deletions

View file

@ -435,7 +435,6 @@ export const MessageUserStore = new class {
id = '', id = '',
plain = '', plain = '',
resultHtml = '', resultHtml = '',
pgpSigned = false,
messagesDom = this.messagesBodiesDom(), messagesDom = this.messagesBodiesDom(),
selectedMessage = this.selectorMessageSelected(), selectedMessage = this.selectorMessageSelected(),
message = this.message(); message = this.message();
@ -495,27 +494,17 @@ export const MessageUserStore = new class {
if ((message.isPgpSigned() || message.isPgpEncrypted()) && PgpUserStore.capaOpenPGP()) { if ((message.isPgpSigned() || message.isPgpEncrypted()) && PgpUserStore.capaOpenPGP()) {
plain = pString(json.Plain); plain = pString(json.Plain);
const isPgpEncrypted = /---BEGIN PGP MESSAGE---/.test(plain);
if (!isPgpEncrypted) {
pgpSigned =
/-----BEGIN PGP SIGNED MESSAGE-----/.test(plain) && /-----BEGIN PGP SIGNATURE-----/.test(plain);
}
const pre = createElement('pre'); const pre = createElement('pre');
if (pgpSigned && message.isPgpSigned()) { if (message.isPgpSigned()) {
pre.className = 'b-plain-openpgp signed'; pre.className = 'b-plain-openpgp signed';
pre.textContent = plain; pre.textContent = plain;
} else if (isPgpEncrypted && message.isPgpEncrypted()) { } else if (message.isPgpEncrypted()) {
pre.className = 'b-plain-openpgp encrypted'; pre.className = 'b-plain-openpgp encrypted';
pre.textContent = plain; pre.textContent = plain;
} else { } else {
pre.innerHTML = resultHtml; pre.innerHTML = resultHtml;
} }
resultHtml = pre.outerHTML; resultHtml = pre.outerHTML;
message.isPgpSigned(pgpSigned);
message.isPgpEncrypted(isPgpEncrypted);
} else { } else {
resultHtml = '<pre>' + resultHtml + '</pre>'; resultHtml = '<pre>' + resultHtml + '</pre>';
} }

View file

@ -355,10 +355,10 @@ export const PgpUserStore = new class {
verControl = Element.fromHTML('<div class="b-openpgp-control"><i class="fontastic">🔒</i></div>'); verControl = Element.fromHTML('<div class="b-openpgp-control"><i class="fontastic">🔒</i></div>');
if (encrypted) { if (encrypted) {
verControl.title = i18n('MESSAGE/PGP_ENCRYPTED_MESSAGE_DESC'); verControl.title = i18n('MESSAGE/PGP_ENCRYPTED_MESSAGE_DESC');
verControl.addEventHandler('click', domControlEncryptedClickHelper(this, dom, domText, recipients)); verControl.addEventListener('click', domControlEncryptedClickHelper(this, dom, domText, recipients));
} else { } else {
verControl.title = i18n('MESSAGE/PGP_SIGNED_MESSAGE_DESC'); verControl.title = i18n('MESSAGE/PGP_SIGNED_MESSAGE_DESC');
verControl.addEventHandler('click', domControlSignedClickHelper(this, dom, domText)); verControl.addEventListener('click', domControlSignedClickHelper(this, dom, domText));
} }
dom.before(verControl, createElement('div')); dom.before(verControl, createElement('div'));

View file

@ -438,19 +438,21 @@
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
color: #777; opacity: 0.5;
&:hover { &:hover {
color: #111; opacity: 1;
} }
&.success { &.success {
color: green; color: green;
cursor: help; cursor: help;
opacity: 1;
} }
&.error { &.error {
color: red; color: red;
opacity: 1;
} }
} }
} }

View file

@ -104,31 +104,24 @@ class FetchResponse
*/ */
public function GetFetchValue(string $sFetchItemName) public function GetFetchValue(string $sFetchItemName)
{ {
$mReturn = null; if (Enumerations\FetchType::INDEX === $sFetchItemName) {
return $this->oImapResponse->ResponseList[1];
}
if (isset($this->oImapResponse->ResponseList[3]) && \is_array($this->oImapResponse->ResponseList[3])) {
$bNextIsValue = false; $bNextIsValue = false;
foreach ($this->oImapResponse->ResponseList[3] as $mItem) {
if (Enumerations\FetchType::INDEX === $sFetchItemName) if ($bNextIsValue) {
{ return $mItem;
$mReturn = $this->oImapResponse->ResponseList[1];
}
else if (isset($this->oImapResponse->ResponseList[3]) && \is_array($this->oImapResponse->ResponseList[3]))
{
foreach ($this->oImapResponse->ResponseList[3] as $mItem)
{
if ($bNextIsValue)
{
$mReturn = $mItem;
break;
} }
if ($sFetchItemName === $mItem) if ($sFetchItemName === $mItem) {
{
$bNextIsValue = true; $bNextIsValue = true;
} }
} }
} }
return $mReturn; return null;
} }
public function GetHeaderFieldsValue(string $sRfc822SubMimeIndex = '') : string public function GetHeaderFieldsValue(string $sRfc822SubMimeIndex = '') : string

View file

@ -281,31 +281,6 @@ class MailClient
$oBodyStructure = null; $oBodyStructure = null;
$oMessage = null; $oMessage = null;
$aBodyPeekMimeIndexes = array();
$aSignatureMimeIndexes = array();
$aFetchResponse = $this->oImapClient->Fetch(array(\MailSo\Imap\Enumerations\FetchType::BODYSTRUCTURE), $iIndex, $bIndexIsUid);
if (0 < \count($aFetchResponse) && isset($aFetchResponse[0]))
{
$oBodyStructure = $aFetchResponse[0]->GetFetchBodyStructure();
if ($oBodyStructure)
{
foreach ($oBodyStructure->SearchHtmlOrPlainParts() as $oPart)
{
$aBodyPeekMimeIndexes[] = array($oPart->PartID(), $oPart->Size());
}
$aSignatureParts = $oBodyStructure->SearchByContentType('application/pgp-signature');
if (is_array($aSignatureParts) && 0 < \count($aSignatureParts))
{
foreach ($aSignatureParts as $oPart)
{
$aSignatureMimeIndexes[] = $oPart->PartID();
}
}
}
}
$aFetchItems = array( $aFetchItems = array(
\MailSo\Imap\Enumerations\FetchType::INDEX, \MailSo\Imap\Enumerations\FetchType::INDEX,
\MailSo\Imap\Enumerations\FetchType::UID, \MailSo\Imap\Enumerations\FetchType::UID,
@ -315,25 +290,31 @@ class MailClient
$this->getEnvelopeOrHeadersRequestString() $this->getEnvelopeOrHeadersRequestString()
); );
if (0 < \count($aBodyPeekMimeIndexes)) $aFetchResponse = $this->oImapClient->Fetch(array(\MailSo\Imap\Enumerations\FetchType::BODYSTRUCTURE), $iIndex, $bIndexIsUid);
if (0 < \count($aFetchResponse) && isset($aFetchResponse[0]))
{ {
foreach ($aBodyPeekMimeIndexes as $aTextMimeData) $oBodyStructure = $aFetchResponse[0]->GetFetchBodyStructure();
if ($oBodyStructure)
{ {
$sLine = \MailSo\Imap\Enumerations\FetchType::BODY_PEEK.'['.$aTextMimeData[0].']'; foreach ($oBodyStructure->SearchHtmlOrPlainParts() as $oPart)
if (0 < $iBodyTextLimit && $iBodyTextLimit < $aTextMimeData[1]) {
$sLine = \MailSo\Imap\Enumerations\FetchType::BODY_PEEK.'['.$oPart->PartID().']';
if (0 < $iBodyTextLimit && $iBodyTextLimit < $oPart->Size())
{ {
$sLine .= "<0.{$iBodyTextLimit}>"; $sLine .= "<0.{$iBodyTextLimit}>";
} }
$aFetchItems[] = $sLine; $aFetchItems[] = $sLine;
} }
}
if (0 < \count($aSignatureMimeIndexes)) $aSignatureParts = $oBodyStructure->SearchByContentType('application/pgp-signature');
if (is_array($aSignatureParts) && 0 < \count($aSignatureParts))
{ {
foreach ($aSignatureMimeIndexes as $sTextMimeIndex) foreach ($aSignatureParts as $oPart)
{ {
$aFetchItems[] = \MailSo\Imap\Enumerations\FetchType::BODY_PEEK.'['.$sTextMimeIndex.']'; $aFetchItems[] = \MailSo\Imap\Enumerations\FetchType::BODY_PEEK.'['.$oPart->PartID().']';
}
}
} }
} }

View file

@ -31,157 +31,61 @@ class Message implements \JsonSerializable
$iHeaderTimeStampInUTC = 0, $iHeaderTimeStampInUTC = 0,
$sHeaderDate = '', $sHeaderDate = '',
$aFlags = [], $aFlags = [],
$aFlagsLowerCase = []; $aFlagsLowerCase = [],
/** /**
* @var \MailSo\Mime\EmailCollection * @var \MailSo\Mime\EmailCollection
*/ */
private $oFrom; $oFrom = null,
$oSender = null,
$oReplyTo = null,
$oDeliveredTo = null,
$oTo = null,
$oCc = null,
$oBcc = null,
/** $sInReplyTo = '',
* @var \MailSo\Mime\EmailCollection
*/
private $oSender;
/** $sPlain = '',
* @var \MailSo\Mime\EmailCollection $sHtml = '',
*/
private $oReplyTo;
/**
* @var \MailSo\Mime\EmailCollection
*/
private $oDeliveredTo;
/**
* @var \MailSo\Mime\EmailCollection
*/
private $oTo;
/**
* @var \MailSo\Mime\EmailCollection
*/
private $oCc;
/**
* @var \MailSo\Mime\EmailCollection
*/
private $oBcc;
/**
* @var string
*/
private $sInReplyTo;
/**
* @var string
*/
private $sPlain;
/**
* @var string
*/
private $sHtml;
/** /**
* @var AttachmentCollection * @var AttachmentCollection
*/ */
private $oAttachments; $oAttachments = null,
/** /**
* @var array * @var array
*/ */
private $aDraftInfo; $aDraftInfo = null,
/** $sReferences = '',
* @var string
*/
private $sReferences;
/** /**
* @var int * @var int
*/ */
private $iSensitivity; $iSensitivity,
$iPriority,
/** $sDeliveryReceipt = '',
* @var int
*/
private $iPriority;
/** $sReadReceipt = '',
* @var string
*/
private $sDeliveryReceipt;
/** $aUnsubsribeLinks = array(),
* @var string
*/
private $sReadReceipt;
/** $aThreads = array(),
* @var array
*/
private $aUnsubsribeLinks;
/** $bTextPartIsTrimmed = false,
* @var array
*/
private $aThreads;
/** $sPgpSignature = '',
* @var bool $sPgpSignatureMicAlg = '',
*/ $bPgpSigned = false,
private $bTextPartIsTrimmed; $bPgpEncrypted = false;
/**
* @var string
*/
private $sPgpSignature;
/**
* @var bool
*/
private $bPgpSigned;
/**
* @var bool
*/
private $bPgpEncrypted;
function __construct() function __construct()
{ {
$this->oFrom = null;
$this->oSender = null;
$this->oReplyTo = null;
$this->oDeliveredTo = null;
$this->oTo = null;
$this->oCc = null;
$this->oBcc = null;
$this->sPlain = '';
$this->sHtml = '';
$this->oAttachments = null;
$this->aDraftInfo = null;
$this->sInReplyTo = '';
$this->sReferences = '';
$this->aUnsubsribeLinks = array();
$this->iSensitivity = \MailSo\Mime\Enumerations\Sensitivity::NOTHING; $this->iSensitivity = \MailSo\Mime\Enumerations\Sensitivity::NOTHING;
$this->iPriority = \MailSo\Mime\Enumerations\MessagePriority::NORMAL; $this->iPriority = \MailSo\Mime\Enumerations\MessagePriority::NORMAL;
$this->sDeliveryReceipt = '';
$this->sReadReceipt = '';
$this->aThreads = array();
$this->bTextPartIsTrimmed = false;
$this->sPgpSignature = '';
$this->bPgpSigned = false;
$this->bPgpEncrypted = false;
return $this;
} }
public function Plain() : string public function Plain() : string
@ -199,6 +103,11 @@ class Message implements \JsonSerializable
return $this->sPgpSignature; return $this->sPgpSignature;
} }
public function PgpSignatureMicAlg() : string
{
return $this->sPgpSignatureMicAlg;
}
public function isPgpSigned() : bool public function isPgpSigned() : bool
{ {
return $this->bPgpSigned; return $this->bPgpSigned;
@ -611,6 +520,24 @@ class Message implements \JsonSerializable
$this->sInReplyTo = $oFetchResponse->GetFetchEnvelopeValue(8, ''); $this->sInReplyTo = $oFetchResponse->GetFetchEnvelopeValue(8, '');
} }
// Content-Type: multipart/signed; micalg="pgp-sha256"; protocol="application/pgp-signature"
if ('multipart/signed' === \strtolower($this->sContentType)
&& 'application/pgp-signature' === \strtolower($oHeaders->ParameterValue(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, \MailSo\Mime\Enumerations\Parameter::PROTOCOL)))
{
$aPgpSignatureParts = $oBodyStructure ? $oBodyStructure->SearchByContentType('application/pgp-signature') : null;
if ($this->bPgpSigned = !empty($aPgpSignatureParts)) {
$sPgpSignatureText = $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::BODY.'['.$aPgpSignatureParts[0]->PartID().']');
if (\is_string($sPgpSignatureText) && 0 < \strlen($sPgpSignatureText) && 0 < \strpos($sPgpSignatureText, 'BEGIN PGP SIGNATURE')) {
$this->sPgpSignature = \trim($sPgpSignatureText);
$this->sPgpSignatureMicAlg = (string) $oHeaders->ParameterValue(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, 'micalg');
}
}
}
// Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"
$this->bPgpEncrypted = ('multipart/encrypted' === \strtolower($this->sContentType)
&& 'application/pgp-encrypted' === \strtolower($oHeaders->ParameterValue(\MailSo\Mime\Enumerations\Header::CONTENT_TYPE, \MailSo\Mime\Enumerations\Parameter::PROTOCOL)));
$aTextParts = $oBodyStructure ? $oBodyStructure->SearchHtmlOrPlainParts() : null; $aTextParts = $oBodyStructure ? $oBodyStructure->SearchHtmlOrPlainParts() : null;
if ($aTextParts) if ($aTextParts)
{ {
@ -674,39 +601,17 @@ class Message implements \JsonSerializable
} }
$aMatch = array(); $aMatch = array();
if (\preg_match('/-----BEGIN PGP SIGNATURE-----(.+)-----END PGP SIGNATURE-----/ism', $this->sPlain, $aMatch) && !empty($aMatch[0])) if (!$this->bPgpSigned && \preg_match('/-----BEGIN PGP SIGNATURE-----(.+)-----END PGP SIGNATURE-----/ism', $this->sPlain, $aMatch) && !empty($aMatch[0]))
{ {
$this->sPgpSignature = \trim($aMatch[0]); $this->sPgpSignature = \trim($aMatch[0]);
$this->bPgpSigned = true; $this->bPgpSigned = true;
} }
$aMatch = array(); $this->bPgpEncrypted = !$this->bPgpEncrypted && false !== \stripos($this->sPlain, '-----BEGIN PGP MESSAGE-----');
if (\preg_match('/-----BEGIN PGP MESSAGE-----/ism', $this->sPlain, $aMatch) && !empty($aMatch[0]))
{
$this->bPgpEncrypted = true;
}
unset($aHtmlParts, $aPlainParts, $aMatch); unset($aHtmlParts, $aPlainParts, $aMatch);
} }
// if (empty($this->sPgpSignature) && 'multipart/signed' === \strtolower($this->sContentType) &&
// 'application/pgp-signature' === \strtolower($oHeaders->ParameterValue(
// \MailSo\Mime\Enumerations\Header::CONTENT_TYPE,
// \MailSo\Mime\Enumerations\Parameter::PROTOCOL
// )))
// {
// $aPgpSignatureParts = $oBodyStructure ? $oBodyStructure->SearchByContentType('application/pgp-signature') : null;
// if (\is_array($aPgpSignatureParts) && 0 < \count($aPgpSignatureParts) && isset($aPgpSignatureParts[0]))
// {
// $sPgpSignatureText = $oFetchResponse->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::BODY.'['.$aPgpSignatureParts[0]->PartID().']');
// if (\is_string($sPgpSignatureText) && 0 < \strlen($sPgpSignatureText) && 0 < \strpos($sPgpSignatureText, 'BEGIN PGP SIGNATURE'))
// {
// $this->sPgpSignature = \trim($sPgpSignatureText);
// $this->bPgpSigned = true;
// }
// }
// }
if ($oBodyStructure) if ($oBodyStructure)
{ {
$aAttachmentsParts = $oBodyStructure->SearchAttachmentsParts(); $aAttachmentsParts = $oBodyStructure->SearchAttachmentsParts();

View file

@ -323,6 +323,7 @@ trait Response
$mResult['isPgpSigned'] = $mResponse->isPgpSigned(); $mResult['isPgpSigned'] = $mResponse->isPgpSigned();
$mResult['isPgpEncrypted'] = $mResponse->isPgpEncrypted(); $mResult['isPgpEncrypted'] = $mResponse->isPgpEncrypted();
$mResult['PgpSignature'] = $mResponse->PgpSignature(); $mResult['PgpSignature'] = $mResponse->PgpSignature();
$mResult['PgpSignatureMicAlg'] = $mResponse->PgpSignatureMicAlg();
unset($sHtml, $sPlain); unset($sHtml, $sPlain);