Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/sun/security/util/ManifestDigester.java
41159 views
1
/*
2
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. 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. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.security.util;
27
28
import java.security.MessageDigest;
29
import java.util.ArrayList;
30
import java.util.HashMap;
31
import java.io.ByteArrayOutputStream;
32
import java.io.OutputStream;
33
import java.io.IOException;
34
import java.util.List;
35
36
import static java.nio.charset.StandardCharsets.UTF_8;
37
38
/**
39
* This class is used to compute digests on sections of the Manifest.
40
* Please note that multiple sections might have the same name, and they
41
* all belong to a single Entry.
42
*/
43
public class ManifestDigester {
44
45
/**
46
* The part "{@code Manifest-Main-Attributes}" of the main attributes
47
* digest header name in a signature file as described in the jar
48
* specification:
49
* <blockquote>{@code x-Digest-Manifest-Main-Attributes}
50
* (where x is the standard name of a {@link MessageDigest} algorithm):
51
* The value of this attribute is the digest value of the main attributes
52
* of the manifest.</blockquote>
53
* @see <a href="{@docRoot}/../specs/jar/jar.html#signature-file">
54
* JAR File Specification, section Signature File</a>
55
* @see #getMainAttsEntry
56
*/
57
public static final String MF_MAIN_ATTRS = "Manifest-Main-Attributes";
58
59
/** the raw bytes of the manifest */
60
private final byte[] rawBytes;
61
62
private final Entry mainAttsEntry;
63
64
/** individual sections by their names */
65
private final HashMap<String, Entry> entries = new HashMap<>();
66
67
/** state returned by findSection */
68
static class Position {
69
int endOfFirstLine; // not including newline character
70
71
int endOfSection; // end of section, not including the blank line
72
// between sections
73
int startOfNext; // the start of the next section
74
}
75
76
/**
77
* find a section in the manifest.
78
*
79
* @param offset should point to the starting offset with in the
80
* raw bytes of the next section.
81
*
82
* @pos set by
83
*
84
* @return false if end of bytes has been reached, otherwise returns
85
* true
86
*/
87
@SuppressWarnings("fallthrough")
88
private boolean findSection(int offset, Position pos)
89
{
90
int i = offset, len = rawBytes.length;
91
int last = offset - 1;
92
int next;
93
boolean allBlank = true;
94
95
/* denotes that a position is not yet assigned.
96
* As a primitive type int it cannot be null
97
* and -1 would be confused with (i - 1) when i == 0 */
98
final int UNASSIGNED = Integer.MIN_VALUE;
99
100
pos.endOfFirstLine = UNASSIGNED;
101
102
while (i < len) {
103
byte b = rawBytes[i];
104
switch(b) {
105
case '\r':
106
if (pos.endOfFirstLine == UNASSIGNED)
107
pos.endOfFirstLine = i-1;
108
if (i < len - 1 && rawBytes[i + 1] == '\n')
109
i++;
110
/* fall through */
111
case '\n':
112
if (pos.endOfFirstLine == UNASSIGNED)
113
pos.endOfFirstLine = i-1;
114
if (allBlank || (i == len-1)) {
115
pos.endOfSection = allBlank ? last : i;
116
pos.startOfNext = i+1;
117
return true;
118
}
119
else {
120
// start of a new line
121
last = i;
122
allBlank = true;
123
}
124
break;
125
default:
126
allBlank = false;
127
break;
128
}
129
i++;
130
}
131
return false;
132
}
133
134
public ManifestDigester(byte[] bytes)
135
{
136
rawBytes = bytes;
137
138
Position pos = new Position();
139
140
if (!findSection(0, pos)) {
141
mainAttsEntry = null;
142
return; // XXX: exception?
143
}
144
145
// create an entry for main attributes
146
mainAttsEntry = new Entry().addSection(new Section(
147
0, pos.endOfSection + 1, pos.startOfNext, rawBytes));
148
149
int start = pos.startOfNext;
150
while(findSection(start, pos)) {
151
int len = pos.endOfFirstLine-start+1;
152
int sectionLen = pos.endOfSection-start+1;
153
int sectionLenWithBlank = pos.startOfNext-start;
154
155
if (len >= 6) { // 6 == "Name: ".length()
156
if (isNameAttr(bytes, start)) {
157
ByteArrayOutputStream nameBuf = new ByteArrayOutputStream();
158
nameBuf.write(bytes, start+6, len-6);
159
160
int i = start + len;
161
if ((i-start) < sectionLen) {
162
if (bytes[i] == '\r'
163
&& i + 1 - start < sectionLen
164
&& bytes[i + 1] == '\n') {
165
i += 2;
166
} else {
167
i += 1;
168
}
169
}
170
171
while ((i-start) < sectionLen) {
172
if (bytes[i++] == ' ') {
173
// name is wrapped
174
int wrapStart = i;
175
while (((i-start) < sectionLen)
176
&& (bytes[i] != '\r')
177
&& (bytes[i] != '\n')) i++;
178
int wrapLen = i - wrapStart;
179
if (i - start < sectionLen) {
180
i++;
181
if (bytes[i - 1] == '\r'
182
&& i - start < sectionLen
183
&& bytes[i] == '\n')
184
i++;
185
}
186
187
nameBuf.write(bytes, wrapStart, wrapLen);
188
} else {
189
break;
190
}
191
}
192
193
entries.computeIfAbsent(nameBuf.toString(UTF_8),
194
dummy -> new Entry())
195
.addSection(new Section(start, sectionLen,
196
sectionLenWithBlank, rawBytes));
197
}
198
}
199
start = pos.startOfNext;
200
}
201
}
202
203
private boolean isNameAttr(byte[] bytes, int start)
204
{
205
return ((bytes[start] == 'N') || (bytes[start] == 'n')) &&
206
((bytes[start+1] == 'a') || (bytes[start+1] == 'A')) &&
207
((bytes[start+2] == 'm') || (bytes[start+2] == 'M')) &&
208
((bytes[start+3] == 'e') || (bytes[start+3] == 'E')) &&
209
(bytes[start+4] == ':') &&
210
(bytes[start+5] == ' ');
211
}
212
213
public static class Entry {
214
215
// One Entry for one name, and one name can have multiple sections.
216
// According to the JAR File Specification: "If there are multiple
217
// individual sections for the same file entry, the attributes in
218
// these sections are merged."
219
private List<Section> sections = new ArrayList<>();
220
boolean oldStyle;
221
222
private Entry addSection(Section sec)
223
{
224
sections.add(sec);
225
return this;
226
}
227
228
/**
229
* Check if the sections (particularly the last one of usually only one)
230
* are properly delimited with a trailing blank line so that another
231
* section can be correctly appended and return {@code true} or return
232
* {@code false} to indicate that reproduction is not advised and should
233
* be carried out with a clean "normalized" newly-written manifest.
234
*
235
* @see #reproduceRaw
236
*/
237
public boolean isProperlyDelimited() {
238
return sections.stream().allMatch(
239
Section::isProperlySectionDelimited);
240
}
241
242
public void reproduceRaw(OutputStream out) throws IOException {
243
for (Section sec : sections) {
244
out.write(sec.rawBytes, sec.offset, sec.lengthWithBlankLine);
245
}
246
}
247
248
public byte[] digest(MessageDigest md)
249
{
250
md.reset();
251
for (Section sec : sections) {
252
if (oldStyle) {
253
Section.doOldStyle(md, sec.rawBytes, sec.offset, sec.lengthWithBlankLine);
254
} else {
255
md.update(sec.rawBytes, sec.offset, sec.lengthWithBlankLine);
256
}
257
}
258
return md.digest();
259
}
260
261
/** Netscape doesn't include the new line. Intel and JavaSoft do */
262
263
public byte[] digestWorkaround(MessageDigest md)
264
{
265
md.reset();
266
for (Section sec : sections) {
267
md.update(sec.rawBytes, sec.offset, sec.length);
268
}
269
return md.digest();
270
}
271
}
272
273
private static class Section {
274
int offset;
275
int length;
276
int lengthWithBlankLine;
277
byte[] rawBytes;
278
279
public Section(int offset, int length,
280
int lengthWithBlankLine, byte[] rawBytes)
281
{
282
this.offset = offset;
283
this.length = length;
284
this.lengthWithBlankLine = lengthWithBlankLine;
285
this.rawBytes = rawBytes;
286
}
287
288
/**
289
* Returns {@code true} if the raw section is terminated with a blank
290
* line so that another section can possibly be appended resulting in a
291
* valid manifest and {@code false} otherwise.
292
*/
293
private boolean isProperlySectionDelimited() {
294
return lengthWithBlankLine > length;
295
}
296
297
private static void doOldStyle(MessageDigest md,
298
byte[] bytes,
299
int offset,
300
int length)
301
{
302
// this is too gross to even document, but here goes
303
// the 1.1 jar verification code ignored spaces at the
304
// end of lines when calculating digests, so that is
305
// what this code does. It only gets called if we
306
// are parsing a 1.1 signed signature file
307
int i = offset;
308
int start = offset;
309
int max = offset + length;
310
int prev = -1;
311
while(i <max) {
312
if ((bytes[i] == '\r') && (prev == ' ')) {
313
md.update(bytes, start, i-start-1);
314
start = i;
315
}
316
prev = bytes[i];
317
i++;
318
}
319
md.update(bytes, start, i-start);
320
}
321
}
322
323
/**
324
* @see #MF_MAIN_ATTRS
325
*/
326
public Entry getMainAttsEntry() {
327
return mainAttsEntry;
328
}
329
330
/**
331
* @see #MF_MAIN_ATTRS
332
*/
333
public Entry getMainAttsEntry(boolean oldStyle) {
334
mainAttsEntry.oldStyle = oldStyle;
335
return mainAttsEntry;
336
}
337
338
public Entry get(String name) {
339
return entries.get(name);
340
}
341
342
public Entry get(String name, boolean oldStyle) {
343
Entry e = get(name);
344
if (e == null && MF_MAIN_ATTRS.equals(name)) {
345
e = getMainAttsEntry();
346
}
347
if (e != null) {
348
e.oldStyle = oldStyle;
349
}
350
return e;
351
}
352
353
public byte[] manifestDigest(MessageDigest md) {
354
md.reset();
355
md.update(rawBytes, 0, rawBytes.length);
356
return md.digest();
357
}
358
359
}
360
361