Path: blob/master/test/jdk/sun/security/util/ManifestDigester/ReproduceRaw.java
41152 views
/*1* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.ByteArrayInputStream;24import java.io.ByteArrayOutputStream;25import java.io.IOException;26import java.security.MessageDigest;27import java.util.ArrayList;28import java.util.List;29import java.util.stream.Collectors;30import java.util.jar.Manifest;3132import sun.security.util.ManifestDigester;3334import org.testng.annotations.DataProvider;35import org.testng.annotations.Factory;36import org.testng.annotations.BeforeMethod;37import org.testng.annotations.Test;3839import static java.nio.charset.StandardCharsets.UTF_8;40import static org.testng.Assert.*;4142/**43* @test44* @bug 821737545* @modules java.base/sun.security.util46* @compile ../../tools/jarsigner/Utils.java47* @run testng ReproduceRaw48* @summary Verifies that {@link ManifestDigester} can reproduce parts of49* manifests in their binary form so that {@link JarSigner} can rely on50* {@link ManifestDigester.Entry#reproduceRaw} to write in a map view51* unmodified entries back also unmodified in their binary form.52* <p>53* See also<ul>54* <li>{@link PreserveRawManifestEntryAndDigest} with end to end tests55* with {@code jarsigner} tool and</li>56* <li>{@link FindHeaderEndVsManifestDigesterFindFirstSection} about57* identifying the binary portion of only main attributes and more extensive58* main attributes digesting tests while this one test here is more about59* reproducing individual sections and that they result in the same60* digests.</li>61* </ul>62*/63public class ReproduceRaw {6465static final boolean VERBOSE = false;6667@DataProvider(name = "parameters")68public static Object[][] parameters() {69List<Object[]> tests = new ArrayList<>();70for (String lineBreak : new String[] { "\n", "\r", "\r\n" }) {71for (boolean oldStyle : new Boolean[] { false, true }) {72for (boolean workaround : new Boolean[] { false, true }) {73tests.add(new Object[] { lineBreak, oldStyle, workaround });74}75}76}77return tests.toArray(new Object[tests.size()][]);78}7980@Factory(dataProvider = "parameters")81public static Object[] createTests(String lineBreak,82boolean oldStyle, boolean digestWorkaround) {83return new Object[]{84new ReproduceRaw(lineBreak, oldStyle, digestWorkaround)85};86}8788final String lineBreak;89final boolean oldStyle;90final boolean digestWorkaround;9192public ReproduceRaw(String lineBreak,93boolean oldStyle, boolean digestWorkaround) {94this.lineBreak = lineBreak;95this.oldStyle = oldStyle;96this.digestWorkaround = digestWorkaround;97}9899@BeforeMethod100public void verbose() {101System.out.println("lineBreak = " +102Utils.escapeStringWithNumbers(lineBreak));103System.out.println("oldStyle = " + oldStyle);104System.out.println("digestWorkaround = " + digestWorkaround);105}106107class EchoMessageDigest extends MessageDigest {108109ByteArrayOutputStream buf;110111EchoMessageDigest() {112super("echo");113}114115@Override116protected void engineReset() {117buf = new ByteArrayOutputStream();118}119120@Override121protected void engineUpdate(byte input) {122buf.write(input);123}124125@Override126protected void engineUpdate(byte[] i, int o, int l) {127buf.write(i, o, l);128}129130@Override protected byte[] engineDigest() {131return buf.toByteArray();132}133134}135136/**137* similar to corresponding part of {@link JarSigner#sign0}138* (stripped down to the code for reproducing the old manifest entry by139* entry which was too difficult to achieve using the real JarSigner code140* in the test here)141*/142byte[] reproduceRawManifest(byte[] mfRawBytes,143boolean mainAttsProperlyDelimited,144boolean sectionProperlyDelimited) throws IOException {145Manifest manifest = new Manifest(new ByteArrayInputStream(mfRawBytes));146ManifestDigester oldMd = new ManifestDigester(mfRawBytes);147ByteArrayOutputStream baos = new ByteArrayOutputStream();148149// main attributes150assertEquals(oldMd.getMainAttsEntry().isProperlyDelimited(),151mainAttsProperlyDelimited);152oldMd.getMainAttsEntry().reproduceRaw(baos);153154// individual sections155for (String key : manifest.getEntries().keySet()) {156assertEquals(oldMd.get(key).isProperlyDelimited(),157sectionProperlyDelimited);158oldMd.get(key).reproduceRaw(baos);159}160161return baos.toByteArray();162}163164static String regExscape(String expr) {165for (int i = 0; i < expr.length(); i++) {166if (expr.charAt(i) == '\r' || expr.charAt(i) == '\n') {167expr = expr.substring(0, i) + "\\" + expr.substring(i++);168}169}170return expr;171}172173byte[] digest(byte[] manifest, String section) {174MessageDigest digester = new EchoMessageDigest();175ManifestDigester md = new ManifestDigester(manifest);176ManifestDigester.Entry entry = section == null ?177md.getMainAttsEntry(oldStyle) : md.get(section, oldStyle);178return digestWorkaround ?179entry.digestWorkaround(digester) :180entry.digest(digester);181}182183void test(byte[] originalManifest, boolean mainAttsProperlyDelimited,184boolean sectionProperlyDelimited) throws Exception {185Utils.echoManifest(originalManifest, "original manifest");186byte[] reproducedManifest = reproduceRawManifest(originalManifest,187mainAttsProperlyDelimited, sectionProperlyDelimited);188Utils.echoManifest(reproducedManifest, "reproduced manifest");189190// The reproduced manifest is not necessarily completely identical to191// the original if it contained superfluous blank lines.192// It's sufficient that the digests are equal and as an additional193// check, the reproduced manifest is here compared to the original194// without more than double line breaks.195if (!lineBreak.repeat(2).equals(new String(originalManifest, UTF_8))) {196assertEquals(197new String(reproducedManifest, UTF_8),198new String(originalManifest, UTF_8).replaceAll(199regExscape(lineBreak) + "(" + regExscape(lineBreak) + ")+",200lineBreak.repeat(2)));201}202203// compare digests of reproduced manifest entries with digests of204// original manifest entries205assertEquals(digest(originalManifest, null),206digest(reproducedManifest, null));207for (String key : new Manifest(new ByteArrayInputStream(208originalManifest)).getEntries().keySet()) {209assertEquals(digest(originalManifest, key),210digest(reproducedManifest, key));211}212213// parse and compare original and reproduced manifests as manifests214assertEquals(new Manifest(new ByteArrayInputStream(originalManifest)),215new Manifest(new ByteArrayInputStream(reproducedManifest)));216}217218void test(byte[] originalManifest, boolean mainAttsProperlyDelimited)219throws Exception {220// assert all individual sections properly delimited particularly useful221// when no individual sections present222test(originalManifest, mainAttsProperlyDelimited, true);223}224225@Test226public void testManifestStartsWithBlankLine() throws Exception {227test(lineBreak.getBytes(UTF_8), true);228test(lineBreak.repeat(2).getBytes(UTF_8), true);229}230231@Test232public void testEOFAndNoLineBreakAfterMainAttributes() throws Exception {233assertThrows(RuntimeException.class, () ->234test("Manifest-Version: 1.0".getBytes(UTF_8), false)235);236}237238@Test239public void testEOFAndNoBlankLineAfterMainAttributes() throws Exception {240test(("Manifest-Version: 1.0" + lineBreak).getBytes(UTF_8), false);241}242243@Test244public void testNormalMainAttributes() throws Exception {245test(("Manifest-Version: 1.0" +246lineBreak.repeat(2)).getBytes(UTF_8), true);247}248249@Test250public void testExtraLineBreakAfterMainAttributes() throws Exception {251test(("Manifest-Version: 1.0" +252lineBreak.repeat(3)).getBytes(UTF_8), true);253}254255@Test256public void testIndividualSectionNoLineBreak() throws Exception {257assertNull(new ManifestDigester((258"Manifest-Version: 1.0" + lineBreak +259lineBreak +260"Name: Section-Name" + lineBreak +261"Key: Value"262).getBytes(UTF_8)).get("Section-Name"));263}264265@Test266public void testIndividualSectionOneLineBreak() throws Exception {267test((268"Manifest-Version: 1.0" + lineBreak +269lineBreak +270"Name: Section-Name" + lineBreak +271"Key: Value" + lineBreak272).getBytes(UTF_8), true, false);273}274275@Test276public void testNormalIndividualSectionTwoLineBreak() throws Exception {277test((278"Manifest-Version: 1.0" + lineBreak +279lineBreak +280"Name: Section-Name" + lineBreak +281"Key: Value" + lineBreak.repeat(2)282).getBytes(UTF_8), true, true);283}284285@Test286public void testExtraLineBreakAfterIndividualSection() throws Exception {287test((288"Manifest-Version: 1.0" + lineBreak +289lineBreak +290"Name: Section-Name" + lineBreak +291"Key: Value" + lineBreak.repeat(3)292).getBytes(UTF_8), true, true);293}294295@Test296public void testIndividualSections() throws Exception {297test((298"Manifest-Version: 1.0" + lineBreak +299lineBreak +300"Name: Section-Name" + lineBreak +301"Key: Value" + lineBreak +302lineBreak +303"Name: Section-Name" + lineBreak +304"Key: Value" + lineBreak +305lineBreak306).getBytes(UTF_8), true, true);307}308309@Test310public void testExtraLineBreakBetweenIndividualSections() throws Exception {311test((312"Manifest-Version: 1.0" + lineBreak +313lineBreak +314"Name: Section-Name" + lineBreak +315"Key: Value" + lineBreak +316lineBreak.repeat(2) +317"Name: Section-Name" + lineBreak +318"Key: Value" + lineBreak +319lineBreak320).getBytes(UTF_8), true, true);321}322323}324325326