summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaolán McNamara <caolanm@redhat.com>2016-10-22 20:38:19 +0100
committerMiklos Vajna <vmiklos@collabora.co.uk>2016-10-26 11:39:41 +0000
commitf13e6133fe0127734fcd4c8be2b1a526e4749656 (patch)
tree8b7a9449c090fd085437f61e0b9703c00fb0c9b6
parenta5ee76c9740836397a250174dc171b36a1c5a958 (diff)
implement CryptoAPI RC4+SHA1 encryption scheme for doc import
there might be other variants out there in practice, but this works for default encrypted doc of word 2013 (cherry picked from commit 4f07175cd03bf0fa42992a06d51aed5b421adcf2) contains... rework things in light of now available documentation (cherry picked from commit 6ac3fb584409912d7a4e944a643bc9180bc03015) Change-Id: I995a0437d4001d63e1c3a821e00c05a2af429356 68c1d9489292e63f709bfbc990ffa5ad7d0f827a Reviewed-on: https://gerrit.libreoffice.org/30168 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
-rw-r--r--sc/source/filter/excel/xicontent.cxx2
-rw-r--r--sw/source/filter/ww8/ww8par.cxx110
2 files changed, 92 insertions, 20 deletions
diff --git a/sc/source/filter/excel/xicontent.cxx b/sc/source/filter/excel/xicontent.cxx
index dc2423f5ea11..05a142d8b1f5 100644
--- a/sc/source/filter/excel/xicontent.cxx
+++ b/sc/source/filter/excel/xicontent.cxx
@@ -1153,7 +1153,7 @@ XclImpDecrypterRef lclReadFilepass8_Strong(XclImpStream& rStream)
rStream.Ignore(nHeaderSize - actualHeaderSize);
info.verifier.saltSize = rStream.ReaduInt32();
- if (info.verifier.saltSize != 16)
+ if (info.verifier.saltSize != msfilter::SALT_LENGTH)
return xDecr;
rStream.Read(&info.verifier.salt, sizeof(info.verifier.salt));
rStream.Read(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
diff --git a/sw/source/filter/ww8/ww8par.cxx b/sw/source/filter/ww8/ww8par.cxx
index 48ad5d0ce09a..325a84271a00 100644
--- a/sw/source/filter/ww8/ww8par.cxx
+++ b/sw/source/filter/ww8/ww8par.cxx
@@ -5477,7 +5477,7 @@ namespace
#define WW_BLOCKSIZE 0x200
- void DecryptRC4(msfilter::MSCodec_Std97& rCtx, SvStream &rIn, SvStream &rOut)
+ void DecryptRC4(msfilter::MSCodec97& rCtx, SvStream &rIn, SvStream &rOut)
{
rIn.Seek(STREAM_SEEK_TO_END);
const sal_Size nLen = rIn.Tell();
@@ -5611,7 +5611,7 @@ namespace
return aEncryptionData;
}
- uno::Sequence< beans::NamedValue > InitStd97Codec( ::msfilter::MSCodec_Std97& rCodec, sal_uInt8 pDocId[16], SfxMedium& rMedium )
+ uno::Sequence< beans::NamedValue > Init97Codec(msfilter::MSCodec97& rCodec, sal_uInt8 pDocId[16], SfxMedium& rMedium)
{
uno::Sequence< beans::NamedValue > aEncryptionData;
const SfxUnoAnyItem* pEncryptionData = SfxItemSet::GetItem<SfxUnoAnyItem>(rMedium.GetItemSet(), SID_ENCRYPTIONDATA, false);
@@ -5639,6 +5639,63 @@ namespace
}
}
+//TO-DO: merge this with lclReadFilepass8_Strong in sc which uses a different
+//stream thing
+static bool lclReadCryptoAPIHeader(msfilter::RC4EncryptionInfo &info, SvStream &rStream)
+{
+ //Its possible there are other variants in existance but these
+ //are the defaults I get with Word 2013
+
+ rStream.ReadUInt32(info.header.flags);
+ if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
+ return false;
+
+ sal_uInt32 nHeaderSize(0);
+ rStream.ReadUInt32(nHeaderSize);
+ sal_uInt32 actualHeaderSize = sizeof(info.header);
+
+ if (nHeaderSize < actualHeaderSize)
+ return false;
+
+ rStream.ReadUInt32(info.header.flags);
+ rStream.ReadUInt32(info.header.sizeExtra);
+ rStream.ReadUInt32(info.header.algId);
+ rStream.ReadUInt32(info.header.algIdHash);
+ rStream.ReadUInt32(info.header.keyBits);
+ rStream.ReadUInt32(info.header.providedType);
+ rStream.ReadUInt32(info.header.reserved1);
+ rStream.ReadUInt32(info.header.reserved2);
+
+ rStream.SeekRel(nHeaderSize - actualHeaderSize);
+
+ rStream.ReadUInt32(info.verifier.saltSize);
+ if (info.verifier.saltSize != msfilter::SALT_LENGTH)
+ return false;
+ rStream.Read(&info.verifier.salt, sizeof(info.verifier.salt));
+ rStream.Read(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
+
+ rStream.ReadUInt32(info.verifier.encryptedVerifierHashSize);
+ if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
+ return false;
+ rStream.Read(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
+
+ // check flags and algorithm IDs, required are AES128 and SHA-1
+ if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
+ return false;
+
+ if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
+ return false;
+
+ if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
+ return false;
+
+ // hash algorithm ID 0 defaults to SHA-1 too
+ if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
+ return false;
+
+ return true;
+}
+
sal_uLong SwWW8ImplReader::LoadThroughDecryption(WW8Glossary *pGloss)
{
sal_uLong nErrRet = 0;
@@ -5663,7 +5720,7 @@ sal_uLong SwWW8ImplReader::LoadThroughDecryption(WW8Glossary *pGloss)
SvFileStream aDecryptData;
bool bDecrypt = false;
- enum {RC4, XOR, Other} eAlgo = Other;
+ enum {RC4CryptoAPI, RC4, XOR, Other} eAlgo = Other;
if (m_pWwFib->fEncrypted && !nErrRet)
{
if (!pGloss)
@@ -5678,10 +5735,12 @@ sal_uLong SwWW8ImplReader::LoadThroughDecryption(WW8Glossary *pGloss)
else
{
m_pTableStream->Seek(0);
- sal_uInt32 nEncType;
- m_pTableStream->ReadUInt32( nEncType );
- if (nEncType == 0x10001)
+ sal_uInt32 nEncType(0);
+ m_pTableStream->ReadUInt32(nEncType);
+ if (nEncType == msfilter::VERSION_INFO_1997_FORMAT)
eAlgo = RC4;
+ else if (nEncType == msfilter::VERSION_INFO_2007_FORMAT || nEncType == msfilter::VERSION_INFO_2007_FORMAT_SP2)
+ eAlgo = RC4CryptoAPI;
}
}
}
@@ -5744,22 +5803,35 @@ sal_uLong SwWW8ImplReader::LoadThroughDecryption(WW8Glossary *pGloss)
}
break;
case RC4:
+ case RC4CryptoAPI:
{
- sal_uInt8 aDocId[ 16 ];
- sal_uInt8 aSaltData[ 16 ];
- sal_uInt8 aSaltHash[ 16 ];
+ std::unique_ptr<msfilter::MSCodec97> xCtx;
+ msfilter::RC4EncryptionInfo info;
+ bool bCouldReadHeaders;
- bool bCouldReadHeaders =
- checkRead(*m_pTableStream, aDocId, 16) &&
- checkRead(*m_pTableStream, aSaltData, 16) &&
- checkRead(*m_pTableStream, aSaltHash, 16);
+ if (eAlgo == RC4)
+ {
+ xCtx.reset(new msfilter::MSCodec_Std97);
+ assert(sizeof(info.verifier.encryptedVerifierHash) >= RTL_DIGEST_LENGTH_MD5);
+ bCouldReadHeaders =
+ checkRead(*m_pTableStream, info.verifier.salt, sizeof(info.verifier.salt)) &&
+ checkRead(*m_pTableStream, info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier)) &&
+ checkRead(*m_pTableStream, info.verifier.encryptedVerifierHash, RTL_DIGEST_LENGTH_MD5);
+ }
+ else
+ {
+ xCtx.reset(new msfilter::MSCodec_CryptoAPI);
+ bCouldReadHeaders = lclReadCryptoAPIHeader(info, *m_pTableStream);
+ }
- msfilter::MSCodec_Std97 aCtx;
// if initialization has failed the EncryptionData should be empty
uno::Sequence< beans::NamedValue > aEncryptionData;
if (bCouldReadHeaders)
- aEncryptionData = InitStd97Codec( aCtx, aDocId, *pMedium );
- if ( aEncryptionData.getLength() && aCtx.VerifyKey( aSaltData, aSaltHash ) )
+ aEncryptionData = Init97Codec(*xCtx, info.verifier.salt, *pMedium);
+ else
+ nErrRet = ERRCODE_SVX_READ_FILTER_CRYPT;
+ if (aEncryptionData.getLength() && xCtx->VerifyKey(info.verifier.encryptedVerifier,
+ info.verifier.encryptedVerifierHash))
{
nErrRet = 0;
@@ -5770,14 +5842,14 @@ sal_uLong SwWW8ImplReader::LoadThroughDecryption(WW8Glossary *pGloss)
sal_uInt8 *pIn = new sal_uInt8[nUnencryptedHdr];
nUnencryptedHdr = m_pStrm->Read(pIn, nUnencryptedHdr);
- DecryptRC4(aCtx, *m_pStrm, aDecryptMain);
+ DecryptRC4(*xCtx, *m_pStrm, aDecryptMain);
aDecryptMain.Seek(0);
aDecryptMain.Write(pIn, nUnencryptedHdr);
delete [] pIn;
pTempTable = MakeTemp(aDecryptTable);
- DecryptRC4(aCtx, *m_pTableStream, aDecryptTable);
+ DecryptRC4(*xCtx, *m_pTableStream, aDecryptTable);
m_pTableStream = &aDecryptTable;
if (!m_pDataStream || m_pDataStream == m_pStrm)
@@ -5785,7 +5857,7 @@ sal_uLong SwWW8ImplReader::LoadThroughDecryption(WW8Glossary *pGloss)
else
{
pTempData = MakeTemp(aDecryptData);
- DecryptRC4(aCtx, *m_pDataStream, aDecryptData);
+ DecryptRC4(*xCtx, *m_pDataStream, aDecryptData);
m_pDataStream = &aDecryptData;
}