Home | History | Annotate | Download | only in util
      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