jdk/src/java.base/share/classes/sun/security/util/ManifestDigester.java
changeset 25859 3317bb8137f4
parent 10336 0bb1999251f8
child 30033 b9c86c17164a
equal deleted inserted replaced
25858:836adbf7a2cd 25859:3317bb8137f4
       
     1 /*
       
     2  * Copyright (c) 1997, 2011, 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     @SuppressWarnings("fallthrough")
       
    66     private boolean findSection(int offset, Position pos)
       
    67     {
       
    68         int i = offset, len = rawBytes.length;
       
    69         int last = offset;
       
    70         int next;
       
    71         boolean allBlank = true;
       
    72 
       
    73         pos.endOfFirstLine = -1;
       
    74 
       
    75         while (i < len) {
       
    76             byte b = rawBytes[i];
       
    77             switch(b) {
       
    78             case '\r':
       
    79                 if (pos.endOfFirstLine == -1)
       
    80                     pos.endOfFirstLine = i-1;
       
    81                 if ((i < len) &&  (rawBytes[i+1] == '\n'))
       
    82                     i++;
       
    83                 /* fall through */
       
    84             case '\n':
       
    85                 if (pos.endOfFirstLine == -1)
       
    86                     pos.endOfFirstLine = i-1;
       
    87                 if (allBlank || (i == len-1)) {
       
    88                     if (i == len-1)
       
    89                         pos.endOfSection = i;
       
    90                     else
       
    91                         pos.endOfSection = last;
       
    92                     pos.startOfNext = i+1;
       
    93                     return true;
       
    94                 }
       
    95                 else {
       
    96                     // start of a new line
       
    97                     last = i;
       
    98                     allBlank = true;
       
    99                 }
       
   100                 break;
       
   101             default:
       
   102                 allBlank = false;
       
   103                 break;
       
   104             }
       
   105             i++;
       
   106         }
       
   107         return false;
       
   108     }
       
   109 
       
   110     public ManifestDigester(byte bytes[])
       
   111     {
       
   112         rawBytes = bytes;
       
   113         entries = new HashMap<String, Entry>();
       
   114 
       
   115         ByteArrayOutputStream baos = new ByteArrayOutputStream();
       
   116 
       
   117         Position pos = new Position();
       
   118 
       
   119         if (!findSection(0, pos))
       
   120             return; // XXX: exception?
       
   121 
       
   122         // create an entry for main attributes
       
   123         entries.put(MF_MAIN_ATTRS,
       
   124                 new Entry(0, pos.endOfSection + 1, pos.startOfNext, rawBytes));
       
   125 
       
   126         int start = pos.startOfNext;
       
   127         while(findSection(start, pos)) {
       
   128             int len = pos.endOfFirstLine-start+1;
       
   129             int sectionLen = pos.endOfSection-start+1;
       
   130             int sectionLenWithBlank = pos.startOfNext-start;
       
   131 
       
   132             if (len > 6) {
       
   133                 if (isNameAttr(bytes, start)) {
       
   134                     StringBuilder nameBuf = new StringBuilder(sectionLen);
       
   135 
       
   136                     try {
       
   137                         nameBuf.append(
       
   138                             new String(bytes, start+6, len-6, "UTF8"));
       
   139 
       
   140                         int i = start + len;
       
   141                         if ((i-start) < sectionLen) {
       
   142                             if (bytes[i] == '\r') {
       
   143                                 i += 2;
       
   144                             } else {
       
   145                                 i += 1;
       
   146                             }
       
   147                         }
       
   148 
       
   149                         while ((i-start) < sectionLen) {
       
   150                             if (bytes[i++] == ' ') {
       
   151                                 // name is wrapped
       
   152                                 int wrapStart = i;
       
   153                                 while (((i-start) < sectionLen)
       
   154                                         && (bytes[i++] != '\n'));
       
   155                                     if (bytes[i-1] != '\n')
       
   156                                         return; // XXX: exception?
       
   157                                     int wrapLen;
       
   158                                     if (bytes[i-2] == '\r')
       
   159                                         wrapLen = i-wrapStart-2;
       
   160                                     else
       
   161                                         wrapLen = i-wrapStart-1;
       
   162 
       
   163                             nameBuf.append(new String(bytes, wrapStart,
       
   164                                                       wrapLen, "UTF8"));
       
   165                             } else {
       
   166                                 break;
       
   167                             }
       
   168                         }
       
   169 
       
   170                         entries.put(nameBuf.toString(),
       
   171                             new Entry(start, sectionLen, sectionLenWithBlank,
       
   172                                 rawBytes));
       
   173 
       
   174                     } catch (java.io.UnsupportedEncodingException uee) {
       
   175                         throw new IllegalStateException(
       
   176                             "UTF8 not available on platform");
       
   177                     }
       
   178                 }
       
   179             }
       
   180             start = pos.startOfNext;
       
   181         }
       
   182     }
       
   183 
       
   184     private boolean isNameAttr(byte bytes[], int start)
       
   185     {
       
   186         return ((bytes[start] == 'N') || (bytes[start] == 'n')) &&
       
   187                ((bytes[start+1] == 'a') || (bytes[start+1] == 'A')) &&
       
   188                ((bytes[start+2] == 'm') || (bytes[start+2] == 'M')) &&
       
   189                ((bytes[start+3] == 'e') || (bytes[start+3] == 'E')) &&
       
   190                (bytes[start+4] == ':') &&
       
   191                (bytes[start+5] == ' ');
       
   192     }
       
   193 
       
   194     public static class Entry {
       
   195         int offset;
       
   196         int length;
       
   197         int lengthWithBlankLine;
       
   198         byte[] rawBytes;
       
   199         boolean oldStyle;
       
   200 
       
   201         public Entry(int offset, int length,
       
   202                      int lengthWithBlankLine, byte[] rawBytes)
       
   203         {
       
   204             this.offset = offset;
       
   205             this.length = length;
       
   206             this.lengthWithBlankLine = lengthWithBlankLine;
       
   207             this.rawBytes = rawBytes;
       
   208         }
       
   209 
       
   210         public byte[] digest(MessageDigest md)
       
   211         {
       
   212             md.reset();
       
   213             if (oldStyle) {
       
   214                 doOldStyle(md,rawBytes, offset, lengthWithBlankLine);
       
   215             } else {
       
   216                 md.update(rawBytes, offset, lengthWithBlankLine);
       
   217             }
       
   218             return md.digest();
       
   219         }
       
   220 
       
   221         private void doOldStyle(MessageDigest md,
       
   222                                 byte[] bytes,
       
   223                                 int offset,
       
   224                                 int length)
       
   225         {
       
   226             // this is too gross to even document, but here goes
       
   227             // the 1.1 jar verification code ignored spaces at the
       
   228             // end of lines when calculating digests, so that is
       
   229             // what this code does. It only gets called if we
       
   230             // are parsing a 1.1 signed signature file
       
   231             int i = offset;
       
   232             int start = offset;
       
   233             int max = offset + length;
       
   234             int prev = -1;
       
   235             while(i <max) {
       
   236                 if ((bytes[i] == '\r') && (prev == ' ')) {
       
   237                     md.update(bytes, start, i-start-1);
       
   238                     start = i;
       
   239                 }
       
   240                 prev = bytes[i];
       
   241                 i++;
       
   242             }
       
   243             md.update(bytes, start, i-start);
       
   244         }
       
   245 
       
   246 
       
   247         /** Netscape doesn't include the new line. Intel and JavaSoft do */
       
   248 
       
   249         public byte[] digestWorkaround(MessageDigest md)
       
   250         {
       
   251             md.reset();
       
   252             md.update(rawBytes, offset, length);
       
   253             return md.digest();
       
   254         }
       
   255     }
       
   256 
       
   257     public Entry get(String name, boolean oldStyle) {
       
   258         Entry e = entries.get(name);
       
   259         if (e != null)
       
   260             e.oldStyle = oldStyle;
       
   261         return e;
       
   262     }
       
   263 
       
   264     public byte[] manifestDigest(MessageDigest md)
       
   265         {
       
   266             md.reset();
       
   267             md.update(rawBytes, 0, rawBytes.length);
       
   268             return md.digest();
       
   269         }
       
   270 
       
   271 }