blob: 8530fc32b369f4b6ad07eabf9d5806db345dd604 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.util;
27
28import java.security.*;
29import java.util.HashMap;
30import java.io.ByteArrayOutputStream;
31
32/**
33 * This class is used to compute digests on sections of the Manifest.
34 */
35public class ManifestDigester {
36
37 public static final String MF_MAIN_ATTRS = "Manifest-Main-Attributes";
38
39 /** the raw bytes of the manifest */
40 private byte rawBytes[];
41
42 /** the offset/length pair for a section */
43 private HashMap<String, Entry> entries; // key is a UTF-8 string
44
45 /** state returned by findSection */
46 static class Position {
47 int endOfFirstLine; // not including newline character
48
49 int endOfSection; // end of section, not including the blank line
50 // between sections
51 int startOfNext; // the start of the next section
52 }
53
54 /**
55 * find a section in the manifest.
56 *
57 * @param offset should point to the starting offset with in the
58 * raw bytes of the next section.
59 *
60 * @pos set by
61 *
62 * @returns false if end of bytes has been reached, otherwise returns
63 * true
64 */
65 private boolean findSection(int offset, Position pos)
66 {
67 int i = offset, len = rawBytes.length;
68 int last = offset;
69 int next;
70 boolean allBlank = true;
71
72 pos.endOfFirstLine = -1;
73
74 while (i < len) {
75 byte b = rawBytes[i];
76 switch(b) {
77 case '\r':
78 if (pos.endOfFirstLine == -1)
79 pos.endOfFirstLine = i-1;
80 if ((i < len) && (rawBytes[i+1] == '\n'))
81 i++;
82 case '\n':
83 if (pos.endOfFirstLine == -1)
84 pos.endOfFirstLine = i-1;
85 if (allBlank || (i == len-1)) {
86 if (i == len-1)
87 pos.endOfSection = i;
88 else
89 pos.endOfSection = last;
90 pos.startOfNext = i+1;
91 return true;
92 }
93 else {
94 // start of a new line
95 last = i;
96 allBlank = true;
97 }
98 break;
99 default:
100 allBlank = false;
101 break;
102 }
103 i++;
104 }
105 return false;
106 }
107
108 public ManifestDigester(byte bytes[])
109 {
110 rawBytes = bytes;
111 entries = new HashMap<String, Entry>();
112
113 ByteArrayOutputStream baos = new ByteArrayOutputStream();
114
115 Position pos = new Position();
116
117 if (!findSection(0, pos))
118 return; // XXX: exception?
119
120 // create an entry for main attributes
121 entries.put(MF_MAIN_ATTRS,
122 new Entry(0, pos.endOfSection + 1, pos.startOfNext, rawBytes));
123
124 int start = pos.startOfNext;
125 while(findSection(start, pos)) {
126 int len = pos.endOfFirstLine-start+1;
127 int sectionLen = pos.endOfSection-start+1;
128 int sectionLenWithBlank = pos.startOfNext-start;
129
130 if (len > 6) {
131 if (isNameAttr(bytes, start)) {
132 StringBuilder nameBuf = new StringBuilder();
133
134 try {
135 nameBuf.append(
136 new String(bytes, start+6, len-6, "UTF8"));
137
138 int i = start + len;
139 if ((i-start) < sectionLen) {
140 if (bytes[i] == '\r') {
141 i += 2;
142 } else {
143 i += 1;
144 }
145 }
146
147 while ((i-start) < sectionLen) {
148 if (bytes[i++] == ' ') {
149 // name is wrapped
150 int wrapStart = i;
151 while (((i-start) < sectionLen)
152 && (bytes[i++] != '\n'));
153 if (bytes[i-1] != '\n')
154 return; // XXX: exception?
155 int wrapLen;
156 if (bytes[i-2] == '\r')
157 wrapLen = i-wrapStart-2;
158 else
159 wrapLen = i-wrapStart-1;
160
161 nameBuf.append(new String(bytes, wrapStart,
162 wrapLen, "UTF8"));
163 } else {
164 break;
165 }
166 }
167
168 entries.put(nameBuf.toString(),
169 new Entry(start, sectionLen, sectionLenWithBlank,
170 rawBytes));
171
172 } catch (java.io.UnsupportedEncodingException uee) {
173 throw new IllegalStateException(
174 "UTF8 not available on platform");
175 }
176 }
177 }
178 start = pos.startOfNext;
179 }
180 }
181
182 private boolean isNameAttr(byte bytes[], int start)
183 {
184 return ((bytes[start] == 'N') || (bytes[start] == 'n')) &&
185 ((bytes[start+1] == 'a') || (bytes[start+1] == 'A')) &&
186 ((bytes[start+2] == 'm') || (bytes[start+2] == 'M')) &&
187 ((bytes[start+3] == 'e') || (bytes[start+3] == 'E')) &&
188 (bytes[start+4] == ':') &&
189 (bytes[start+5] == ' ');
190 }
191
192 public static class Entry {
193 int offset;
194 int length;
195 int lengthWithBlankLine;
196 byte[] rawBytes;
197 boolean oldStyle;
198
199 public Entry(int offset, int length,
200 int lengthWithBlankLine, byte[] rawBytes)
201 {
202 this.offset = offset;
203 this.length = length;
204 this.lengthWithBlankLine = lengthWithBlankLine;
205 this.rawBytes = rawBytes;
206 }
207
208 public byte[] digest(MessageDigest md)
209 {
210 md.reset();
211 if (oldStyle) {
212 doOldStyle(md,rawBytes, offset, lengthWithBlankLine);
213 } else {
214 md.update(rawBytes, offset, lengthWithBlankLine);
215 }
216 return md.digest();
217 }
218
219 private void doOldStyle(MessageDigest md,
220 byte[] bytes,
221 int offset,
222 int length)
223 {
224 // this is too gross to even document, but here goes
225 // the 1.1 jar verification code ignored spaces at the
226 // end of lines when calculating digests, so that is
227 // what this code does. It only gets called if we
228 // are parsing a 1.1 signed signature file
229 int i = offset;
230 int start = offset;
231 int max = offset + length;
232 int prev = -1;
233 while(i <max) {
234 if ((bytes[i] == '\r') && (prev == ' ')) {
235 md.update(bytes, start, i-start-1);
236 start = i;
237 }
238 prev = bytes[i];
239 i++;
240 }
241 md.update(bytes, start, i-start);
242 }
243
244
245 /** Netscape doesn't include the new line. Intel and JavaSoft do */
246
247 public byte[] digestWorkaround(MessageDigest md)
248 {
249 md.reset();
250 md.update(rawBytes, offset, length);
251 return md.digest();
252 }
253 }
254
255 public Entry get(String name, boolean oldStyle) {
256 Entry e = entries.get(name);
257 if (e != null)
258 e.oldStyle = oldStyle;
259 return e;
260 }
261
262 public byte[] manifestDigest(MessageDigest md)
263 {
264 md.reset();
265 md.update(rawBytes, 0, rawBytes.length);
266 return md.digest();
267 }
268
269}