001: /*
002: * @(#)ManifestDigester.java 1.21 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.security.util;
029:
030: import java.security.*;
031: import java.util.HashMap;
032:
033: /**
034: * This class is used to compute digests on sections of the Manifest.
035: */
036: public class ManifestDigester {
037:
038: /** the raw bytes of the manifest */
039: byte rawBytes[];
040:
041: /** the offset/length pair for a section */
042: HashMap entries;
043:
044: /** state returned by findSection */
045: static class Position {
046: int endOfFirstLine; // not including newline character
047:
048: int endOfSection; // end of section, not including the blank line
049: // between sections
050: int startOfNext; // the start of the next section
051: }
052:
053: /**
054: * find a section in the manifest.
055: *
056: * @param offset should point to the starting offset with in the
057: * raw bytes of the next section.
058: *
059: * @pos set by
060: *
061: * @returns false if end of bytes has been reached, otherwise returns
062: * true
063: */
064: private boolean findSection(int offset, Position pos) {
065: int i = offset, len = rawBytes.length;
066: int last = offset;
067: int next;
068: boolean allBlank = true;
069:
070: pos.endOfFirstLine = -1;
071:
072: while (i < len) {
073: byte b = rawBytes[i];
074: switch (b) {
075: case '\r':
076: if (pos.endOfFirstLine == -1)
077: pos.endOfFirstLine = i - 1;
078: if ((i < len) && (rawBytes[i + 1] == '\n'))
079: i++;
080: case '\n':
081: if (pos.endOfFirstLine == -1)
082: pos.endOfFirstLine = i - 1;
083: if (allBlank || (i == len - 1)) {
084: if (i == len - 1)
085: pos.endOfSection = i;
086: else
087: pos.endOfSection = last;
088: pos.startOfNext = i + 1;
089: return true;
090: } else {
091: // start of a new line
092: last = i;
093: allBlank = true;
094: }
095: break;
096: default:
097: allBlank = false;
098: break;
099: }
100: i++;
101: }
102: return false;
103: }
104:
105: public ManifestDigester(byte bytes[]) {
106: rawBytes = bytes;
107: entries = new HashMap();
108:
109: // first skip the main attributes
110: Position pos = new Position();
111:
112: if (!findSection(0, pos))
113: return; // exception?
114:
115: int start = pos.startOfNext;
116:
117: while (findSection(start, pos)) {
118: int len = pos.endOfFirstLine - start + 1;
119: int sectionLen = pos.endOfSection - start + 1;
120: int sectionLenWithBlank = pos.startOfNext - start;
121:
122: if (len > 6) {
123: if (isNameAttr(bytes, start)) {
124: StringBuffer nameBuf = new StringBuffer();
125: nameBuf
126: .append(new String(bytes, start + 6,
127: len - 6));
128:
129: int i = start + len;
130: if ((i - start) < sectionLen) {
131: if (bytes[i] == '\r') {
132: i += 2;
133: } else {
134: i += 1;
135: }
136: }
137:
138: while ((i - start) < sectionLen) {
139: if (bytes[i++] == ' ') {
140: // name is wrapped
141: int wrapStart = i;
142: while (((i - start) < sectionLen)
143: && (bytes[i++] != '\n'))
144: ;
145: if (bytes[i - 1] != '\n')
146: return; // exception?
147: int wrapLen;
148: if (bytes[i - 2] == '\r')
149: wrapLen = i - wrapStart - 2;
150: else
151: wrapLen = i - wrapStart - 1;
152: nameBuf.append(new String(bytes, wrapStart,
153: wrapLen));
154: } else {
155: break;
156: }
157: }
158:
159: String name = nameBuf.toString();
160: entries.put(name, new Entry(start, sectionLen,
161: sectionLenWithBlank, rawBytes));
162: }
163: }
164: start = pos.startOfNext;
165: }
166: }
167:
168: private boolean isNameAttr(byte bytes[], int start) {
169: return ((bytes[start] == 'N') || (bytes[start] == 'n'))
170: && ((bytes[start + 1] == 'a') || (bytes[start + 1] == 'A'))
171: && ((bytes[start + 2] == 'm') || (bytes[start + 2] == 'M'))
172: && ((bytes[start + 3] == 'e') || (bytes[start + 3] == 'E'))
173: && (bytes[start + 4] == ':')
174: && (bytes[start + 5] == ' ');
175: }
176:
177: public static class Entry {
178: int offset;
179: int length;
180: int lengthWithBlankLine;
181: byte[] rawBytes;
182: boolean oldStyle;
183:
184: public Entry(int offset, int length, int lengthWithBlankLine,
185: byte[] rawBytes) {
186: this .offset = offset;
187: this .length = length;
188: this .lengthWithBlankLine = lengthWithBlankLine;
189: this .rawBytes = rawBytes;
190: }
191:
192: public byte[] digest(MessageDigest md) {
193: md.reset();
194: if (oldStyle) {
195: doOldStyle(md, rawBytes, offset, lengthWithBlankLine);
196: } else {
197: md.update(rawBytes, offset, lengthWithBlankLine);
198: }
199: return md.digest();
200: }
201:
202: private void doOldStyle(MessageDigest md, byte[] bytes,
203: int offset, int length) {
204: // this is too gross to even document, but here goes
205: // the 1.1 jar verification code ignored spaces at the
206: // end of lines when calculating digests, so that is
207: // what this code does. It only gets called if we
208: // are parsing a 1.1 signed signature file
209: int i = offset;
210: int start = offset;
211: int max = offset + length;
212: int prev = -1;
213: while (i < max) {
214: if ((bytes[i] == '\r') && (prev == ' ')) {
215: md.update(bytes, start, i - start - 1);
216: start = i;
217: }
218: prev = bytes[i];
219: i++;
220: }
221: md.update(bytes, start, i - start);
222: }
223:
224: /** Netscape doesn't include the new line. Others do. */
225:
226: public byte[] digestWorkaround(MessageDigest md) {
227: md.reset();
228: md.update(rawBytes, offset, length);
229: return md.digest();
230: }
231: }
232:
233: public Entry get(String name, boolean oldStyle) {
234: Entry e = (Entry) entries.get(name);
235: if (e != null)
236: e.oldStyle = oldStyle;
237: return e;
238: }
239:
240: public byte[] manifestDigest(MessageDigest md) {
241: md.reset();
242: md.update(rawBytes, 0, rawBytes.length);
243: return md.digest();
244: }
245:
246: }
|