Path: blob/master/test/jdk/sun/security/util/ManifestDigester/DigestInput.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.ByteArrayOutputStream;24import java.io.IOException;25import java.security.MessageDigest;26import java.util.ArrayList;27import java.util.Arrays;28import java.util.List;29import java.util.jar.Attributes.Name;30import java.util.stream.Collectors;3132import sun.security.util.ManifestDigester;3334import org.testng.annotations.DataProvider;35import org.testng.annotations.Factory;36import org.testng.annotations.BeforeMethod;37import org.testng.annotations.AfterTest;38import org.testng.annotations.Test;3940import static java.nio.charset.StandardCharsets.UTF_8;41import static org.testng.Assert.*;4243/**44* @test45* @bug 821737546* @modules java.base/sun.security.util47* @compile ../../tools/jarsigner/Utils.java48* @run testng DigestInput49* @summary Checks that the manifest main attributes and entry digests are the50* same as before resolution of bug 8217375 which means they treat some white51* space different for oldStyle or digestWorkaround except for the blank line52* at the end of the manifest file for digestWorkaround.53*/54public class DigestInput {5556/**57* Filters some test cases for calibrating expected digests with previous58* implementation. TODO: Delete this after calibrating with old sources.59*/60static final boolean FIXED_8217375 = true; // FIXME6162/**63* {@link ManifestDigester.Entry#digestWorkaround} should not feed the64* trailing blank line into the digester. Before resolution of 8217375 it65* fed the trailing blank line into the digest if the second line break66* was at the end of the file due to <pre>67* if (allBlank || (i == len-1)) {68* if (i == len-1)69* pos.endOfSection = i;70* else71* pos.endOfSection = last;72* </pre> in {@link ManifestDigester#findSection}. In that case at the end73* of the manifest file, {@link ManifestDigester.Entry#digestWorkaround}74* would have produced the same digest as75* {@link ManifestDigester.Entry#digest} which was wrong and without effect76* at best.77* <p>78* Once this fix is accepted, this flag can be removed along with79* {@link #assertDigestEqualsCatchWorkaroundBroken}.80*/81static final boolean FIXED_8217375_EOF_ENDOFSECTION = FIXED_8217375;8283static final String SECTION_NAME = "some individual section name";8485@DataProvider(name = "parameters")86public static Object[][] parameters() {87List<Object[]> tests = new ArrayList<>();88for (String lineBreak : new String[] { "\n", "\r", "\r\n" }) {89if ("\r".equals(lineBreak) && !FIXED_8217375) continue;90for (int addLB = 0; addLB <= 4; addLB++) {91for (int numSecs = 0; numSecs <= 4; numSecs++) {92for (boolean otherSec : new Boolean[] { false, true }) {93for (boolean oldStyle : new Boolean[] { false, true }) {94for (boolean workaround :95new Boolean[] { false, true }) {96tests.add(new Object[] {97lineBreak, addLB, numSecs, otherSec,98oldStyle, workaround99});100}101}102}103}104}105}106return tests.toArray(new Object[tests.size()][]);107}108109@Factory(dataProvider = "parameters")110public static Object[] createTests(111String lineBreak, int additionalLineBreaks,112int numberOfSections, boolean hasOtherSection,113boolean oldStyle, boolean digestWorkaround) {114return new Object[] { new DigestInput(lineBreak,115additionalLineBreaks, numberOfSections, hasOtherSection,116oldStyle, digestWorkaround)117};118}119120final String lineBreak;121final int additionalLineBreaks; // number of blank lines delimiting section122final int numberOfSections;123final boolean hasOtherSection;124final boolean oldStyle;125final boolean digestWorkaround;126127public DigestInput(128String lineBreak, int additionalLineBreaks,129int numberOfSections, boolean hasOtherSection,130boolean oldStyle, boolean digestWorkaround) {131this.lineBreak = lineBreak;132this.additionalLineBreaks = additionalLineBreaks;133this.numberOfSections = numberOfSections;134this.hasOtherSection = hasOtherSection;135this.oldStyle = oldStyle;136this.digestWorkaround = digestWorkaround;137}138139@BeforeMethod140public void verbose() {141System.out.println("-".repeat(72));142System.out.println("lineBreak = " +143Utils.escapeStringWithNumbers(lineBreak));144System.out.println("additionalLineBreaks = " + additionalLineBreaks);145System.out.println("numberOfSections = " + numberOfSections);146System.out.println("hasOtherSection = " + hasOtherSection);147System.out.println("oldStyle = " + oldStyle);148System.out.println("digestWorkaround = " + digestWorkaround);149System.out.println("-".repeat(72));150}151152byte[] rawManifestBytes() {153return (154Name.MANIFEST_VERSION + ": 1.0" + lineBreak +155"OldStyle0: no trailing space" + lineBreak +156"OldStyle1: trailing space " + lineBreak +157"OldStyle2: two trailing spaces " + lineBreak +158lineBreak.repeat(additionalLineBreaks) +159(160"Name: " + SECTION_NAME + lineBreak +161"OldStyle0: no trailing space" + lineBreak +162"OldStyle1: trailing space " + lineBreak +163"OldStyle2: two trailing spaces " + lineBreak +164lineBreak.repeat(additionalLineBreaks)165).repeat(numberOfSections) +166(hasOtherSection ?167"Name: unrelated trailing section" + lineBreak +168"OldStyle0: no trailing space" + lineBreak +169"OldStyle1: trailing space " + lineBreak +170"OldStyle2: two trailing spaces " + lineBreak +171lineBreak.repeat(additionalLineBreaks)172: "")173).getBytes(UTF_8);174}175176byte[] expectedMainAttrsDigest(boolean digestWorkaround) {177return (178Name.MANIFEST_VERSION + ": 1.0" + lineBreak +179"OldStyle0: no trailing space" + lineBreak +180"OldStyle1: trailing space" +181(!oldStyle || !lineBreak.startsWith("\r") || digestWorkaround ?182" " : "") + lineBreak +183"OldStyle2: two trailing spaces " +184(!oldStyle || !lineBreak.startsWith("\r") || digestWorkaround ?185" " : "") + lineBreak +186(187(188!digestWorkaround189|| (190additionalLineBreaks == 1191&& numberOfSections == 0192&& !hasOtherSection193&& (194digestWorkaround195&& !FIXED_8217375_EOF_ENDOFSECTION196)197)198) && (199additionalLineBreaks > 0200|| numberOfSections > 0201|| hasOtherSection202)203? lineBreak : "")204).getBytes(UTF_8);205}206207byte[] expectedIndividualSectionDigest(boolean digestWorkaround) {208if (numberOfSections == 0) return null;209return (210(211"Name: " + SECTION_NAME + lineBreak +212"OldStyle0: no trailing space" + lineBreak +213"OldStyle1: trailing space" +214(!oldStyle || !lineBreak.startsWith("\r")215|| digestWorkaround ? " " : "") + lineBreak +216"OldStyle2: two trailing spaces " +217(!oldStyle || !lineBreak.startsWith("\r")218|| digestWorkaround ? " " : "") + lineBreak +219(220(221!digestWorkaround222) && (223additionalLineBreaks > 0224)225? lineBreak : "")226).repeat(numberOfSections) +227(228additionalLineBreaks == 1229&& !hasOtherSection230&& digestWorkaround231&& !FIXED_8217375_EOF_ENDOFSECTION232? lineBreak : "")233).getBytes(UTF_8);234}235236class EchoMessageDigest extends MessageDigest {237238ByteArrayOutputStream buf;239240EchoMessageDigest() {241super("echo");242}243244@Override245protected void engineReset() {246buf = new ByteArrayOutputStream();247}248249@Override250protected void engineUpdate(byte input) {251buf.write(input);252}253254@Override255protected void engineUpdate(byte[] i, int o, int l) {256buf.write(i, o, l);257}258259@Override protected byte[] engineDigest() {260return buf.toByteArray();261}262263}264265byte[] digestMainAttributes(byte[] mfBytes) throws Exception {266Utils.echoManifest(mfBytes, "going to digest main attributes of");267268ManifestDigester md = new ManifestDigester(mfBytes);269ManifestDigester.Entry entry =270md.get(ManifestDigester.MF_MAIN_ATTRS, oldStyle);271MessageDigest digester = new EchoMessageDigest();272return digestWorkaround ?273entry.digestWorkaround(digester) : entry.digest(digester);274}275276byte[] digestIndividualSection(byte[] mfBytes) throws Exception {277Utils.echoManifest(mfBytes,278"going to digest section " + SECTION_NAME + " of");279280ManifestDigester md = new ManifestDigester(mfBytes);281ManifestDigester.Entry entry = md.get(SECTION_NAME, oldStyle);282if (entry == null) {283return null;284}285MessageDigest digester = new EchoMessageDigest();286return digestWorkaround ?287entry.digestWorkaround(digester) : entry.digest(digester);288}289290291/**292* Checks that the manifest main attributes digest is the same as before.293*/294@Test295public void testMainAttributesDigest() throws Exception {296byte[] mfRaw = rawManifestBytes();297byte[] digest = digestMainAttributes(mfRaw);298byte[] expectedDigest = expectedMainAttrsDigest(digestWorkaround);299300// the individual section will be digested along with the main301// attributes if not properly delimited with a blank line302if (additionalLineBreaks == 0303&& (numberOfSections > 0 || hasOtherSection)) {304assertNotEquals(digest, expectedDigest);305return;306}307308byte[] expectedDigestNoWorkaround = expectedMainAttrsDigest(false);309310// assertDigestEquals(digest, expectedDigest); // FIXME311assertDigestEqualsCatchWorkaroundBroken(312digest, expectedDigest, expectedDigestNoWorkaround);313}314315/**316* Checks that an individual section digest is the same as before.317*/318@Test319public void testIndividualSectionDigest() throws Exception {320byte[] mfRaw = rawManifestBytes();321byte[] digest = digestIndividualSection(mfRaw);322323// no digest will be produced for an individual section that is not324// properly section delimited with a blank line.325byte[] expectedDigest =326additionalLineBreaks == 0 ? null :327expectedIndividualSectionDigest(digestWorkaround);328329byte[] expectedDigestNoWorkaround =330additionalLineBreaks == 0 ? null :331expectedIndividualSectionDigest(false);332333// assertDigestEquals(digest, expectedDigest); // FIXME334assertDigestEqualsCatchWorkaroundBroken(335digest, expectedDigest, expectedDigestNoWorkaround);336}337338static int firstDiffPos = Integer.MAX_VALUE;339340/**341* @see FIXED_8217375_EOF_ENDOFSECTION342*/343void assertDigestEqualsCatchWorkaroundBroken(344byte[] actual, byte[] expected, byte[] expectedNoWorkaround)345throws IOException {346try {347assertDigestEquals(actual, expected);348} catch (AssertionError e) {349if (digestWorkaround && FIXED_8217375_EOF_ENDOFSECTION &&350Arrays.equals(expected, expectedNoWorkaround)) {351// if digests with and without workaround are the same anyway352// the workaround has failed and could not have worked with353// the same digest as produced without workaround before354// which would not match either because equal.355return;356}357fail("failed also without digestWorkaound", e);358}359}360361void assertDigestEquals(byte[] actual, byte[] expected) throws IOException {362if (actual == null && expected == null) return;363Utils.echoManifest(actual, "actual digest");364Utils.echoManifest(expected, "expected digest");365for (int i = 0; i < actual.length && i < expected.length; i++) {366if (actual[i] != expected[i]) {367firstDiffPos = Math.min(firstDiffPos, i);368verbose();369fail("found first difference in current test"370+ " at position " + i);371}372}373if (actual.length != expected.length) {374int diffPos = Math.min(actual.length, expected.length);375firstDiffPos = Math.min(firstDiffPos, diffPos);376verbose();377fail("found first difference in current test"378+ " at position " + diffPos + " after one digest end");379}380assertEquals(actual, expected);381}382383@AfterTest384public void reportFirstDiffPos() {385System.err.println("found first difference in all tests"386+ " at position " + firstDiffPos);387}388389}390391392