src/java.base/share/classes/sun/security/util/ManifestDigester.java
changeset 57488 94691d8e746f
parent 49778 3e6ce108d269
equal deleted inserted replaced
57487:643978a35f6e 57488:94691d8e746f
     1 /*
     1 /*
     2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
     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.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 package sun.security.util;
    26 package sun.security.util;
    27 
    27 
    28 import java.security.*;
    28 import java.security.MessageDigest;
    29 import java.util.ArrayList;
    29 import java.util.ArrayList;
    30 import java.util.HashMap;
    30 import java.util.HashMap;
    31 import java.io.ByteArrayOutputStream;
    31 import java.io.ByteArrayOutputStream;
       
    32 import java.io.OutputStream;
       
    33 import java.io.IOException;
    32 import java.util.List;
    34 import java.util.List;
    33 
    35 
    34 import static java.nio.charset.StandardCharsets.UTF_8;
    36 import static java.nio.charset.StandardCharsets.UTF_8;
    35 
    37 
    36 /**
    38 /**
    38  * Please note that multiple sections might have the same name, and they
    40  * Please note that multiple sections might have the same name, and they
    39  * all belong to a single Entry.
    41  * all belong to a single Entry.
    40  */
    42  */
    41 public class ManifestDigester {
    43 public class ManifestDigester {
    42 
    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      */
    43     public static final String MF_MAIN_ATTRS = "Manifest-Main-Attributes";
    57     public static final String MF_MAIN_ATTRS = "Manifest-Main-Attributes";
    44 
    58 
    45     /** the raw bytes of the manifest */
    59     /** the raw bytes of the manifest */
    46     private byte[] rawBytes;
    60     private final byte[] rawBytes;
    47 
    61 
    48     /** the entries grouped by names */
    62     private final Entry mainAttsEntry;
    49     private HashMap<String, Entry> entries; // key is a UTF-8 string
    63 
       
    64     /** individual sections by their names */
       
    65     private final HashMap<String, Entry> entries = new HashMap<>();
    50 
    66 
    51     /** state returned by findSection */
    67     /** state returned by findSection */
    52     static class Position {
    68     static class Position {
    53         int endOfFirstLine; // not including newline character
    69         int endOfFirstLine; // not including newline character
    54 
    70 
    70      */
    86      */
    71     @SuppressWarnings("fallthrough")
    87     @SuppressWarnings("fallthrough")
    72     private boolean findSection(int offset, Position pos)
    88     private boolean findSection(int offset, Position pos)
    73     {
    89     {
    74         int i = offset, len = rawBytes.length;
    90         int i = offset, len = rawBytes.length;
    75         int last = offset;
    91         int last = offset - 1;
    76         int next;
    92         int next;
    77         boolean allBlank = true;
    93         boolean allBlank = true;
    78 
    94 
    79         pos.endOfFirstLine = -1;
    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;
    80 
   101 
    81         while (i < len) {
   102         while (i < len) {
    82             byte b = rawBytes[i];
   103             byte b = rawBytes[i];
    83             switch(b) {
   104             switch(b) {
    84             case '\r':
   105             case '\r':
    85                 if (pos.endOfFirstLine == -1)
   106                 if (pos.endOfFirstLine == UNASSIGNED)
    86                     pos.endOfFirstLine = i-1;
   107                     pos.endOfFirstLine = i-1;
    87                 if ((i < len) &&  (rawBytes[i+1] == '\n'))
   108                 if (i < len - 1 && rawBytes[i + 1] == '\n')
    88                     i++;
   109                     i++;
    89                 /* fall through */
   110                 /* fall through */
    90             case '\n':
   111             case '\n':
    91                 if (pos.endOfFirstLine == -1)
   112                 if (pos.endOfFirstLine == UNASSIGNED)
    92                     pos.endOfFirstLine = i-1;
   113                     pos.endOfFirstLine = i-1;
    93                 if (allBlank || (i == len-1)) {
   114                 if (allBlank || (i == len-1)) {
    94                     if (i == len-1)
   115                     pos.endOfSection = allBlank ? last : i;
    95                         pos.endOfSection = i;
       
    96                     else
       
    97                         pos.endOfSection = last;
       
    98                     pos.startOfNext = i+1;
   116                     pos.startOfNext = i+1;
    99                     return true;
   117                     return true;
   100                 }
   118                 }
   101                 else {
   119                 else {
   102                     // start of a new line
   120                     // start of a new line
   114     }
   132     }
   115 
   133 
   116     public ManifestDigester(byte[] bytes)
   134     public ManifestDigester(byte[] bytes)
   117     {
   135     {
   118         rawBytes = bytes;
   136         rawBytes = bytes;
   119         entries = new HashMap<>();
       
   120 
   137 
   121         Position pos = new Position();
   138         Position pos = new Position();
   122 
   139 
   123         if (!findSection(0, pos))
   140         if (!findSection(0, pos)) {
       
   141             mainAttsEntry = null;
   124             return; // XXX: exception?
   142             return; // XXX: exception?
       
   143         }
   125 
   144 
   126         // create an entry for main attributes
   145         // create an entry for main attributes
   127         entries.put(MF_MAIN_ATTRS, new Entry().addSection(
   146         mainAttsEntry = new Entry().addSection(new Section(
   128                 new Section(0, pos.endOfSection + 1, pos.startOfNext, rawBytes)));
   147                 0, pos.endOfSection + 1, pos.startOfNext, rawBytes));
   129 
   148 
   130         int start = pos.startOfNext;
   149         int start = pos.startOfNext;
   131         while(findSection(start, pos)) {
   150         while(findSection(start, pos)) {
   132             int len = pos.endOfFirstLine-start+1;
   151             int len = pos.endOfFirstLine-start+1;
   133             int sectionLen = pos.endOfSection-start+1;
   152             int sectionLen = pos.endOfSection-start+1;
   134             int sectionLenWithBlank = pos.startOfNext-start;
   153             int sectionLenWithBlank = pos.startOfNext-start;
   135 
   154 
   136             if (len > 6) {
   155             if (len >= 6) { // 6 == "Name: ".length()
   137                 if (isNameAttr(bytes, start)) {
   156                 if (isNameAttr(bytes, start)) {
   138                     ByteArrayOutputStream nameBuf = new ByteArrayOutputStream();
   157                     ByteArrayOutputStream nameBuf = new ByteArrayOutputStream();
   139                     nameBuf.write(bytes, start+6, len-6);
   158                     nameBuf.write(bytes, start+6, len-6);
   140 
   159 
   141                     int i = start + len;
   160                     int i = start + len;
   142                     if ((i-start) < sectionLen) {
   161                     if ((i-start) < sectionLen) {
   143                         if (bytes[i] == '\r') {
   162                         if (bytes[i] == '\r'
       
   163                                 && i + 1 - start < sectionLen
       
   164                                 && bytes[i + 1] == '\n') {
   144                             i += 2;
   165                             i += 2;
   145                         } else {
   166                         } else {
   146                             i += 1;
   167                             i += 1;
   147                         }
   168                         }
   148                     }
   169                     }
   150                     while ((i-start) < sectionLen) {
   171                     while ((i-start) < sectionLen) {
   151                         if (bytes[i++] == ' ') {
   172                         if (bytes[i++] == ' ') {
   152                             // name is wrapped
   173                             // name is wrapped
   153                             int wrapStart = i;
   174                             int wrapStart = i;
   154                             while (((i-start) < sectionLen)
   175                             while (((i-start) < sectionLen)
   155                                     && (bytes[i++] != '\n'));
   176                                     && (bytes[i] != '\r')
   156                             if (bytes[i-1] != '\n')
   177                                     && (bytes[i] != '\n')) i++;
   157                                 return; // XXX: exception?
   178                             int wrapLen = i - wrapStart;
   158                             int wrapLen;
   179                             if (i - start < sectionLen) {
   159                             if (bytes[i-2] == '\r')
   180                                 i++;
   160                                 wrapLen = i-wrapStart-2;
   181                                 if (bytes[i - 1] == '\r'
   161                             else
   182                                     && i - start < sectionLen
   162                                 wrapLen = i-wrapStart-1;
   183                                     && bytes[i] == '\n')
       
   184                                         i++;
       
   185                             }
   163 
   186 
   164                             nameBuf.write(bytes, wrapStart, wrapLen);
   187                             nameBuf.write(bytes, wrapStart, wrapLen);
   165                         } else {
   188                         } else {
   166                             break;
   189                             break;
   167                         }
   190                         }
   168                     }
   191                     }
   169 
   192 
   170                     entries.computeIfAbsent(new String(nameBuf.toByteArray(), UTF_8),
   193                     entries.computeIfAbsent(nameBuf.toString(UTF_8),
   171                                             dummy -> new Entry())
   194                                             dummy -> new Entry())
   172                             .addSection(new Section(start, sectionLen,
   195                             .addSection(new Section(start, sectionLen,
   173                                     sectionLenWithBlank, rawBytes));
   196                                     sectionLenWithBlank, rawBytes));
   174                 }
   197                 }
   175             }
   198             }
   200         {
   223         {
   201             sections.add(sec);
   224             sections.add(sec);
   202             return this;
   225             return this;
   203         }
   226         }
   204 
   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 
   205         public byte[] digest(MessageDigest md)
   248         public byte[] digest(MessageDigest md)
   206         {
   249         {
   207             md.reset();
   250             md.reset();
   208             for (Section sec : sections) {
   251             for (Section sec : sections) {
   209                 if (oldStyle) {
   252                 if (oldStyle) {
   238         {
   281         {
   239             this.offset = offset;
   282             this.offset = offset;
   240             this.length = length;
   283             this.length = length;
   241             this.lengthWithBlankLine = lengthWithBlankLine;
   284             this.lengthWithBlankLine = lengthWithBlankLine;
   242             this.rawBytes = rawBytes;
   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;
   243         }
   295         }
   244 
   296 
   245         private static void doOldStyle(MessageDigest md,
   297         private static void doOldStyle(MessageDigest md,
   246                                 byte[] bytes,
   298                                 byte[] bytes,
   247                                 int offset,
   299                                 int offset,
   266             }
   318             }
   267             md.update(bytes, start, i-start);
   319             md.update(bytes, start, i-start);
   268         }
   320         }
   269     }
   321     }
   270 
   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 
   271     public Entry get(String name, boolean oldStyle) {
   342     public Entry get(String name, boolean oldStyle) {
   272         Entry e = entries.get(name);
   343         Entry e = get(name);
   273         if (e != null)
   344         if (e == null && MF_MAIN_ATTRS.equals(name)) {
       
   345             e = getMainAttsEntry();
       
   346         }
       
   347         if (e != null) {
   274             e.oldStyle = oldStyle;
   348             e.oldStyle = oldStyle;
       
   349         }
   275         return e;
   350         return e;
   276     }
   351     }
   277 
   352 
   278     public byte[] manifestDigest(MessageDigest md) {
   353     public byte[] manifestDigest(MessageDigest md) {
   279         md.reset();
   354         md.reset();