1 /* 2 * Copyright (c) 1997, 2009, 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.*; 29 import java.util.HashMap; 30 import java.io.ByteArrayOutputStream; 31 32 /** 33 * This class is used to compute digests on sections of the Manifest. 34 */ 35 public 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(sectionLen); 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 } 270