Skip to content

Commit

Permalink
feat: restrict obfuscation to font core media types
Browse files Browse the repository at this point in the history
EPUB 3.3 explicitly restricts the font obfuscation algorithm to resources
being declared as dont Core Media Types.

This commit:
- registers obfuscated resources to the OCFPackage when parsing the
  `encyrption.xml` file (with parsing location)
- checks that if obfuscated resources are declared in Package Documents,
  they have a font core media type
- add and tweak tests

Fix #1291
  • Loading branch information
rdeltour committed Jan 24, 2022
1 parent 73fe57c commit a229edf
Show file tree
Hide file tree
Showing 29 changed files with 141 additions and 23 deletions.
Expand Up @@ -302,6 +302,7 @@ private void initialize()
severities.put(MessageId.PKG_023, Severity.USAGE);
severities.put(MessageId.PKG_024, Severity.INFO);
severities.put(MessageId.PKG_025, Severity.ERROR);
severities.put(MessageId.PKG_026, Severity.ERROR);

// Resources
severities.put(MessageId.RSC_001, Severity.ERROR);
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/adobe/epubcheck/messages/MessageId.java
Expand Up @@ -296,6 +296,7 @@ public enum MessageId implements Comparable<MessageId>
PKG_023("PKG-023"),
PKG_024("PKG-024"),
PKG_025("PKG-025"),
PKG_026("PKG-026"),

// Messages relating to resources
RSC_001("RSC-001"),
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/com/adobe/epubcheck/ocf/EncryptionHandler.java
Expand Up @@ -22,14 +22,15 @@

package com.adobe.epubcheck.ocf;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

import com.adobe.epubcheck.api.EPUBLocation;
import com.adobe.epubcheck.util.HandlerUtil;
import com.adobe.epubcheck.xml.XMLElement;
import com.adobe.epubcheck.xml.XMLHandler;
import com.adobe.epubcheck.xml.XMLParser;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

public class EncryptionHandler implements XMLHandler
{
private final OCFPackage ocf;
Expand Down Expand Up @@ -83,6 +84,7 @@ public void startElement()
if (algorithm.equals("http://www.idpf.org/2008/embedding"))
{
ocf.setEncryption(entryName, new IDPFFontManglingFilter(null));
ocf.setObfuscated(entryName, parser.getLocation());
}
else if (algorithm.equals("http://ns.adobe.com/pdf/enc#RC"))
{
Expand Down
26 changes: 25 additions & 1 deletion src/main/java/com/adobe/epubcheck/ocf/OCFChecker.java
Expand Up @@ -51,9 +51,11 @@
import com.adobe.epubcheck.api.Report;
import com.adobe.epubcheck.messages.MessageId;
import com.adobe.epubcheck.opf.OPFChecker;
import com.adobe.epubcheck.opf.OPFChecker30;
import com.adobe.epubcheck.opf.OPFData;
import com.adobe.epubcheck.opf.OPFHandler;
import com.adobe.epubcheck.opf.OPFHandler30;
import com.adobe.epubcheck.opf.OPFItem;
import com.adobe.epubcheck.opf.ValidationContext;
import com.adobe.epubcheck.opf.ValidationContext.ValidationContextBuilder;
import com.adobe.epubcheck.util.CheckUtil;
Expand All @@ -64,6 +66,7 @@
import com.adobe.epubcheck.xml.XMLParser;
import com.adobe.epubcheck.xml.XMLValidator;
import com.adobe.epubcheck.xml.XMLValidators;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
Expand Down Expand Up @@ -349,7 +352,7 @@ else if (!normalizedEntriesSet.add(Normalizer.normalize(entry, Form.NFC)))
for (final String entry : ocf.getFileEntries())
{
ocf.reportMetadata(entry, report);

// if the entry is not in the whitelist (META-INF/* + mimetype)
// and not declared in (one of) the OPF document(s)
if (!entry.startsWith("META-INF/") && !entry.startsWith("META-INF\\")
Expand All @@ -371,6 +374,27 @@ public boolean apply(OPFHandler opfHandler)
report.message(MessageId.OPF_003, EPUBLocation.create(ocf.getName()), entry);
}
OCFFilenameChecker.checkCompatiblyEscaped(entry, report, validationVersion);

// check obfuscated resource are Font Core Media Types
if (ocf.isObfuscatedFont(entry))
{
for (OPFHandler opf : opfHandlers)
{
// try to find the first Package Document where the entry is
// declared
Optional<OPFItem> item = opf.getItemByPath(entry);
if (item.isPresent())
{
// report if it is not a font core media type
if (!OPFChecker30.isBlessedFontType(item.get().getMimeType()))
{
report.message(MessageId.PKG_026, ocf.getObfuscationDeclarationLocation(entry),
item.get().getMimeType(), opf.getPath());
}
break;
}
}
}
}

// check all directory entries without duplicates
Expand Down
34 changes: 28 additions & 6 deletions src/main/java/com/adobe/epubcheck/ocf/OCFPackage.java
Expand Up @@ -4,7 +4,6 @@
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -26,7 +25,8 @@

public abstract class OCFPackage implements GenericResourceProvider
{
final Hashtable<String, EncryptionFilter> enc;
final Map<String, EncryptionFilter> enc = new HashMap<>();
final Map<String, EPUBLocation> obfuscated = new HashMap<>();
String uniqueIdentifier;
private Report reporter;
private final Supplier<OCFData> ocfData = Suppliers.memoize(new Supplier<OCFData>()
Expand Down Expand Up @@ -72,16 +72,17 @@ public Map<String, OPFData> get()
}
});

public OCFPackage()
{
this.enc = new Hashtable<String, EncryptionFilter>();
}

public void setEncryption(String name, EncryptionFilter encryptionFilter)
{
enc.put(name, encryptionFilter);
}

public void setObfuscated(String name, EPUBLocation location)
{
obfuscated.put(name, location);
}

/**
* @param name
* the name of a relative file that is possibly in the container
Expand Down Expand Up @@ -133,6 +134,27 @@ public boolean canDecrypt(String fileName)
return filter == null || filter.canDecrypt();
}

/**
* @param path path of the resource to test
* @return true if that resource is encrypted with the font obfuscation algorithm
*/
public boolean isObfuscatedFont(String path)
{
return obfuscated.containsKey(path);
}

/**
* Returns the location in META-INF/encryption.xml where the given resource
* is declared as an obfuscated font, or <code>null</code> if it is not.
*
* @param path path of the resource to test
* @return the location in META-INF/encryption.xml, or null
*/
public EPUBLocation getObfuscationDeclarationLocation(String path)
{
return obfuscated.get(path);
}

/**
* This method parses the container entry and stores important data, but does
* /not/ validate the container against a schema definition.
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/adobe/epubcheck/opf/OPFHandler.java
Expand Up @@ -127,6 +127,10 @@ public OPFHandler(ValidationContext context, XMLParser parser)
this.report = context.report;
this.parser = parser;
}

public String getPath() {
return path;
}

public boolean getOpf12PackageFile()
{
Expand Down
Expand Up @@ -313,7 +313,8 @@ PKG_022=Wrong file extension for image. The image is a "%1$s" file but has the f
PKG_023=Validating the EPUB against version 2.0, default validation profile will be used.
PKG_024=Uncommon EPUB file extension.
PKG_024_SUG=For maximum compatibility, use ".epub".
PKG_025=Publication resource must not be located in the META-INF directory
PKG_025=Publication resource must not be located in the META-INF directory
PKG_026=Obfuscated resource must be a Font Core Media Type (was declared as "%1$s" in "%2$s").

#Resources
RSC_001=File "%1$s" could not be found.
Expand Down
22 changes: 14 additions & 8 deletions src/test/resources/epub3/container-publication.feature
Expand Up @@ -119,9 +119,10 @@ Feature: EPUB 3 ▸ Open Container Format ▸ Full Publication Checks
And the message contains 'expected element "encryption"'
And no other errors or warnings are reported

Scenario: Report an unknown encryption scheme
Scenario: Verify encryption can be used
(but file will not be parsed)
Given the reporting level is set to INFO
When checking EPUB 'ocf-encryption-unknown-error'
When checking EPUB 'ocf-encryption-unknown-valid'
Then info RSC-004 is reported
And no other errors or warnings are reported

Expand Down Expand Up @@ -180,16 +181,21 @@ Feature: EPUB 3 ▸ Open Container Format ▸ Full Publication Checks


## 5. Resource Obfuscation
Scenario: Verify a publication with obfuscated resource (here a font file)

Scenario: Verify a publication with obfuscated font
When checking EPUB 'ocf-obfuscation-valid'
Then no errors or warnings are reported

Scenario: Verify a publication with obfuscation of a usually-parsed resource (here an SVG image)
When checking EPUB 'ocf-obfuscation-svg-valid'
Then info RSC-004 is reported
Scenario: Report an obfuscated font that is not a Core Media Type
When checking EPUB 'ocf-obfuscation-not-cmt-error'
Then error PKG-026 is reported
And no errors or warnings are reported


Scenario: Report an obfuscated font that is not a font
When checking EPUB 'ocf-obfuscation-not-font-error'
Then error PKG-026 is reported
And no errors or warnings are reported


## C. the 'application/epub+zip' Media Type

Expand Down
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8"/>
<title>Minimal EPUB</title>
</head>
<body>
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en" lang="en">
<head>
<meta charset="utf-8"/>
<title>Minimal Nav</title>
</head>
<body>
<nav epub:type="toc">
<ol>
<li><a href="content_001.xhtml">content 001</a></li>
</ol>
</nav>
</body>
</html>
Binary file not shown.
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="en" unique-identifier="q">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title id="title">Minimal EPUB 3.0</dc:title>
<dc:language>en</dc:language>
<dc:identifier id="q">NOID</dc:identifier>
<meta property="dcterms:modified">2017-06-14T00:00:01Z</meta>
</metadata>
<manifest>
<item id="content_001" href="content_001.xhtml" media-type="application/xhtml+xml"/>
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
<item id="font" href="obfuscated-font.otf" media-type="font/opentype"/>
</manifest>
<spine>
<itemref idref="content_001" />
</spine>
</package>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
<rootfiles>
<rootfile full-path="EPUB/package.opf" media-type="application/oebps-package+xml"/>
</rootfiles>
</container>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<encryption xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
<EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.idpf.org/2008/embedding"/>
<CipherData>
<CipherReference URI="EPUB/obfuscated-font.otf"/>
</CipherData>
</EncryptedData>
</encryption>
Expand Up @@ -7,6 +7,5 @@
<body epub:type="bodymatter">
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
<img src="emoji.svg" alt="a smiling emoji" />
</body>
</html>
Expand Up @@ -9,7 +9,7 @@
<manifest>
<item id="content_001" href="content_001.xhtml" media-type="application/xhtml+xml"/>
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
<item id="emoji" href="emoji.svg" media-type="image/svg+xml"/>
<item id="doc" href="doc.xml" media-type="application/xml"/>
</manifest>
<spine>
<itemref idref="content_001" />
Expand Down
Expand Up @@ -4,7 +4,7 @@
<enc:EncryptedData>
<enc:EncryptionMethod Algorithm="http://www.idpf.org/2008/embedding"/>
<enc:CipherData>
<enc:CipherReference URI="EPUB/emoji.svg"/>
<enc:CipherReference URI="EPUB/doc.xml"/>
</enc:CipherData>
</enc:EncryptedData>
</encryption>
@@ -0,0 +1 @@
application/epub+zip
Expand Up @@ -9,7 +9,7 @@
<manifest>
<item id="content_001" href="content_001.xhtml" media-type="application/xhtml+xml"/>
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
<item id="font" href="obfuscated-font.otf" media-type="font/opentype"/>
<item id="font" href="obfuscated-font.otf" media-type="font/otf"/>
</manifest>
<spine>
<itemref idref="content_001" />
Expand Down

0 comments on commit a229edf

Please sign in to comment.