Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/util/ManifestDigester.java
38830 views
/*1* Copyright (c) 1997, 2011, 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. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.util;2627import java.security.*;28import java.util.ArrayList;29import java.util.HashMap;30import java.io.ByteArrayOutputStream;31import java.util.List;3233/**34* This class is used to compute digests on sections of the Manifest.35*/36public class ManifestDigester {3738public static final String MF_MAIN_ATTRS = "Manifest-Main-Attributes";3940/** the raw bytes of the manifest */41private byte rawBytes[];4243/** the entries grouped by names */44private HashMap<String, Entry> entries; // key is a UTF-8 string4546/** state returned by findSection */47static class Position {48int endOfFirstLine; // not including newline character4950int endOfSection; // end of section, not including the blank line51// between sections52int startOfNext; // the start of the next section53}5455/**56* find a section in the manifest.57*58* @param offset should point to the starting offset with in the59* raw bytes of the next section.60*61* @pos set by62*63* @returns false if end of bytes has been reached, otherwise returns64* true65*/66@SuppressWarnings("fallthrough")67private boolean findSection(int offset, Position pos)68{69int i = offset, len = rawBytes.length;70int last = offset;71int next;72boolean allBlank = true;7374pos.endOfFirstLine = -1;7576while (i < len) {77byte b = rawBytes[i];78switch(b) {79case '\r':80if (pos.endOfFirstLine == -1)81pos.endOfFirstLine = i-1;82if ((i < len) && (rawBytes[i+1] == '\n'))83i++;84/* fall through */85case '\n':86if (pos.endOfFirstLine == -1)87pos.endOfFirstLine = i-1;88if (allBlank || (i == len-1)) {89if (i == len-1)90pos.endOfSection = i;91else92pos.endOfSection = last;93pos.startOfNext = i+1;94return true;95}96else {97// start of a new line98last = i;99allBlank = true;100}101break;102default:103allBlank = false;104break;105}106i++;107}108return false;109}110111public ManifestDigester(byte bytes[])112{113rawBytes = bytes;114entries = new HashMap<String, Entry>();115116ByteArrayOutputStream baos = new ByteArrayOutputStream();117118Position pos = new Position();119120if (!findSection(0, pos))121return; // XXX: exception?122123// create an entry for main attributes124entries.put(MF_MAIN_ATTRS, new Entry().addSection(125new Section(0, pos.endOfSection + 1, pos.startOfNext, rawBytes)));126127int start = pos.startOfNext;128while(findSection(start, pos)) {129int len = pos.endOfFirstLine-start+1;130int sectionLen = pos.endOfSection-start+1;131int sectionLenWithBlank = pos.startOfNext-start;132133if (len > 6) {134if (isNameAttr(bytes, start)) {135StringBuilder nameBuf = new StringBuilder(sectionLen);136137try {138nameBuf.append(139new String(bytes, start+6, len-6, "UTF8"));140141int i = start + len;142if ((i-start) < sectionLen) {143if (bytes[i] == '\r') {144i += 2;145} else {146i += 1;147}148}149150while ((i-start) < sectionLen) {151if (bytes[i++] == ' ') {152// name is wrapped153int wrapStart = i;154while (((i-start) < sectionLen)155&& (bytes[i++] != '\n'));156if (bytes[i-1] != '\n')157return; // XXX: exception?158int wrapLen;159if (bytes[i-2] == '\r')160wrapLen = i-wrapStart-2;161else162wrapLen = i-wrapStart-1;163164nameBuf.append(new String(bytes, wrapStart,165wrapLen, "UTF8"));166} else {167break;168}169}170171Entry e = entries.get(nameBuf.toString());172if (e == null) {173entries.put(nameBuf.toString(), new Entry()174.addSection(new Section(start, sectionLen,175sectionLenWithBlank, rawBytes)));176} else {177e.addSection(new Section(start, sectionLen,178sectionLenWithBlank, rawBytes));179}180181} catch (java.io.UnsupportedEncodingException uee) {182throw new IllegalStateException(183"UTF8 not available on platform");184}185}186}187start = pos.startOfNext;188}189}190191private boolean isNameAttr(byte bytes[], int start)192{193return ((bytes[start] == 'N') || (bytes[start] == 'n')) &&194((bytes[start+1] == 'a') || (bytes[start+1] == 'A')) &&195((bytes[start+2] == 'm') || (bytes[start+2] == 'M')) &&196((bytes[start+3] == 'e') || (bytes[start+3] == 'E')) &&197(bytes[start+4] == ':') &&198(bytes[start+5] == ' ');199}200201public static class Entry {202203// One Entry for one name, and one name can have multiple sections.204// According to the JAR File Specification: "If there are multiple205// individual sections for the same file entry, the attributes in206// these sections are merged."207private List<Section> sections = new ArrayList<>();208boolean oldStyle;209210private Entry addSection(Section sec)211{212sections.add(sec);213return this;214}215216public byte[] digest(MessageDigest md)217{218md.reset();219for (Section sec : sections) {220if (oldStyle) {221Section.doOldStyle(md, sec.rawBytes, sec.offset, sec.lengthWithBlankLine);222} else {223md.update(sec.rawBytes, sec.offset, sec.lengthWithBlankLine);224}225}226return md.digest();227}228229/** Netscape doesn't include the new line. Intel and JavaSoft do */230231public byte[] digestWorkaround(MessageDigest md)232{233md.reset();234for (Section sec : sections) {235md.update(sec.rawBytes, sec.offset, sec.length);236}237return md.digest();238}239}240241private static class Section {242int offset;243int length;244int lengthWithBlankLine;245byte[] rawBytes;246247public Section(int offset, int length,248int lengthWithBlankLine, byte[] rawBytes)249{250this.offset = offset;251this.length = length;252this.lengthWithBlankLine = lengthWithBlankLine;253this.rawBytes = rawBytes;254}255256private static void doOldStyle(MessageDigest md,257byte[] bytes,258int offset,259int length)260{261// this is too gross to even document, but here goes262// the 1.1 jar verification code ignored spaces at the263// end of lines when calculating digests, so that is264// what this code does. It only gets called if we265// are parsing a 1.1 signed signature file266int i = offset;267int start = offset;268int max = offset + length;269int prev = -1;270while(i <max) {271if ((bytes[i] == '\r') && (prev == ' ')) {272md.update(bytes, start, i-start-1);273start = i;274}275prev = bytes[i];276i++;277}278md.update(bytes, start, i-start);279}280}281282public Entry get(String name, boolean oldStyle) {283Entry e = entries.get(name);284if (e != null)285e.oldStyle = oldStyle;286return e;287}288289public byte[] manifestDigest(MessageDigest md)290{291md.reset();292md.update(rawBytes, 0, rawBytes.length);293return md.digest();294}295296}297298299