1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * https://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.commons.compress.archivers.zip; 20 21 import java.util.Arrays; 22 import java.util.Collections; 23 import java.util.HashMap; 24 import java.util.Map; 25 import java.util.zip.ZipException; 26 27 /** 28 * Base class for all PKWare strong crypto extra headers. 29 * 30 * <p> 31 * This base class acts as a marker so you know you can ignore all extra fields that extend this class if you are not interested in the meta data of PKWare 32 * strong encryption. 33 * </p> 34 * 35 * <strong>Algorithm IDs</strong> - integer identifier of the encryption algorithm from the following range 36 * 37 * <ul> 38 * <li>0x6601 - DES</li> 39 * <li>0x6602 - RC2 (version needed to extract < 5.2)</li> 40 * <li>0x6603 - 3DES 168</li> 41 * <li>0x6609 - 3DES 112</li> 42 * <li>0x660E - AES 128</li> 43 * <li>0x660F - AES 192</li> 44 * <li>0x6610 - AES 256</li> 45 * <li>0x6702 - RC2 (version needed to extract >= 5.2)</li> 46 * <li>0x6720 - Blowfish</li> 47 * <li>0x6721 - Twofish</li> 48 * <li>0x6801 - RC4</li> 49 * <li>0xFFFF - Unknown algorithm</li> 50 * </ul> 51 * 52 * <strong>Hash Algorithms</strong> - integer identifier of the hash algorithm from the following range 53 * 54 * <ul> 55 * <li>0x0000 - none</li> 56 * <li>0x0001 - CRC32</li> 57 * <li>0x8003 - MD5</li> 58 * <li>0x8004 - SHA1</li> 59 * <li>0x8007 - RIPEMD160</li> 60 * <li>0x800C - SHA256</li> 61 * <li>0x800D - SHA384</li> 62 * <li>0x800E - SHA512</li> 63 * </ul> 64 * 65 * @since 1.11 66 */ 67 public abstract class PKWareExtraHeader implements ZipExtraField { 68 69 /** 70 * Enumerates encryption algorithm. 71 * 72 * @since 1.11 73 */ 74 public enum EncryptionAlgorithm { 75 76 /** 77 * DES with code 0x6601. 78 */ 79 DES(0x6601), 80 81 /** 82 * RC2pre52 with code 0x6602. 83 */ 84 RC2pre52(0x6602), 85 86 /** 87 * TripleDES168 with code 0x6603. 88 */ 89 TripleDES168(0x6603), 90 91 /** 92 * TripleDES192 with code 0x6609. 93 */ 94 TripleDES192(0x6609), 95 96 /** 97 * AES128 with code 0x660E. 98 */ 99 AES128(0x660E), 100 101 /** 102 * AES192 with code 0x660F. 103 */ 104 AES192(0x660F), 105 106 /** 107 * AES256 with code 0x6610. 108 */ 109 AES256(0x6610), 110 111 /** 112 * RC2 with code 0x6702. 113 */ 114 RC2(0x6702), 115 116 /** 117 * RC4 with code 0x6801. 118 */ 119 RC4(0x6801), 120 121 /** 122 * UNKNOWN with code 0xFFFF. 123 */ 124 UNKNOWN(0xFFFF); 125 126 private static final Map<Integer, EncryptionAlgorithm> codeToEnum; 127 128 static { 129 final Map<Integer, EncryptionAlgorithm> cte = new HashMap<>(); 130 for (final EncryptionAlgorithm method : values()) { 131 cte.put(method.getCode(), method); 132 } 133 codeToEnum = Collections.unmodifiableMap(cte); 134 } 135 136 /** 137 * Returns the EncryptionAlgorithm for the given code or null if the method is not known. 138 * 139 * @param code the code of the algorithm 140 * @return the EncryptionAlgorithm for the given code or null if the method is not known 141 */ 142 public static EncryptionAlgorithm getAlgorithmByCode(final int code) { 143 return codeToEnum.get(code); 144 } 145 146 private final int code; 147 148 /** 149 * Constructs a new instance. 150 */ 151 EncryptionAlgorithm(final int code) { 152 this.code = code; 153 } 154 155 /** 156 * Gets the algorithm ID. 157 * 158 * @return the PKWare AlgorithmId 159 */ 160 public int getCode() { 161 return code; 162 } 163 } 164 165 /** 166 * Enumerates hash Algorithm 167 * 168 * @since 1.11 169 */ 170 public enum HashAlgorithm { 171 172 /** 173 * NONE with code 0. 174 */ 175 NONE(0), 176 177 /** 178 * CRC32 with code 1. 179 */ 180 CRC32(1), 181 182 /** 183 * MD5 with code 0x8003. 184 */ 185 MD5(0x8003), 186 187 /** 188 * SHA1 with code 0x8004. 189 */ 190 SHA1(0x8004), 191 192 /** 193 * RIPEND160 with code 0x8007. 194 */ 195 RIPEND160(0x8007), 196 197 /** 198 * SHA256 with code 0x800C. 199 */ 200 SHA256(0x800C), 201 202 /** 203 * SHA384 with code 0x800D. 204 */ 205 SHA384(0x800D), 206 207 /** 208 * SHA512 with code 0x800E. 209 */ 210 SHA512(0x800E); 211 212 private static final Map<Integer, HashAlgorithm> codeToEnum; 213 214 static { 215 final Map<Integer, HashAlgorithm> cte = new HashMap<>(); 216 for (final HashAlgorithm method : values()) { 217 cte.put(method.getCode(), method); 218 } 219 codeToEnum = Collections.unmodifiableMap(cte); 220 } 221 222 /** 223 * Returns the HashAlgorithm for the given code or null if the method is not known. 224 * 225 * @param code the code of the algorithm 226 * @return the HashAlgorithm for the given code or null if the method is not known 227 */ 228 public static HashAlgorithm getAlgorithmByCode(final int code) { 229 return codeToEnum.get(code); 230 } 231 232 private final int code; 233 234 /** 235 * Constructs a new instance. 236 */ 237 HashAlgorithm(final int code) { 238 this.code = code; 239 } 240 241 /** 242 * Gets the hash algorithm ID. 243 * 244 * @return the PKWare hashAlg 245 */ 246 public int getCode() { 247 return code; 248 } 249 } 250 251 private final ZipShort headerId; 252 253 /** 254 * Extra field data in local file data - without Header-ID or length specifier. 255 */ 256 private byte[] localData; 257 258 /** 259 * Extra field data in central directory - without Header-ID or length specifier. 260 */ 261 private byte[] centralData; 262 263 /** 264 * Constructs a new instance. 265 * 266 * @param headerId The header ID. 267 */ 268 protected PKWareExtraHeader(final ZipShort headerId) { 269 this.headerId = headerId; 270 } 271 272 /** 273 * Asserts the given length is greater or equal to the given minimum. 274 * 275 * @param minimum the minimum. 276 * @param length the length. 277 * @throws ZipException Thrown if the length is less than the minimum. 278 */ 279 protected final void assertMinimalLength(final int minimum, final int length) throws ZipException { 280 if (length < minimum) { 281 throw new ZipException(getClass().getName() + " is too short, only " + length + " bytes, expected at least " + minimum); 282 } 283 } 284 285 /** 286 * Gets the central data. 287 * 288 * @return the central data if present, else return the local file data 289 */ 290 @Override 291 public byte[] getCentralDirectoryData() { 292 if (centralData != null) { 293 return ZipUtil.copy(centralData); 294 } 295 return getLocalFileDataData(); 296 } 297 298 /** 299 * Gets the central data length. If there is no central data, get the local file data length. 300 * 301 * @return the central data length 302 */ 303 @Override 304 public ZipShort getCentralDirectoryLength() { 305 if (centralData != null) { 306 return new ZipShort(centralData.length); 307 } 308 return getLocalFileDataLength(); 309 } 310 311 /** 312 * Gets the header id. 313 * 314 * @return the header id 315 */ 316 @Override 317 public ZipShort getHeaderId() { 318 return headerId; 319 } 320 321 /** 322 * Gets the local data. 323 * 324 * @return the local data 325 */ 326 @Override 327 public byte[] getLocalFileDataData() { 328 return ZipUtil.copy(localData); 329 } 330 331 /** 332 * Gets the length of the local data. 333 * 334 * @return the length of the local data. 335 */ 336 @Override 337 public ZipShort getLocalFileDataLength() { 338 return ZipShort.lengthOf(localData); 339 } 340 341 /** 342 * @param data the array of bytes. 343 * @param offset the source location in the data array. 344 * @param length the number of bytes to use in the data array. 345 * @see ZipExtraField#parseFromCentralDirectoryData(byte[], int, int) 346 */ 347 @Override 348 public void parseFromCentralDirectoryData(final byte[] data, final int offset, final int length) throws ZipException { 349 final byte[] tmp = Arrays.copyOfRange(data, offset, offset + length); 350 setCentralDirectoryData(tmp); 351 if (localData == null) { 352 setLocalFileDataData(tmp); 353 } 354 } 355 356 /** 357 * @param data the array of bytes. 358 * @param offset the source location in the data array. 359 * @param length the number of bytes to use in the data array. 360 * @see ZipExtraField#parseFromLocalFileData(byte[], int, int) 361 */ 362 @Override 363 public void parseFromLocalFileData(final byte[] data, final int offset, final int length) throws ZipException { 364 setLocalFileDataData(Arrays.copyOfRange(data, offset, offset + length)); 365 } 366 367 /** 368 * Sets the extra field data in central directory. 369 * 370 * @param data the data to use 371 */ 372 public void setCentralDirectoryData(final byte[] data) { 373 centralData = ZipUtil.copy(data); 374 } 375 376 /** 377 * Sets the extra field data in the local file data - without Header-ID or length specifier. 378 * 379 * @param data the field data to use 380 */ 381 public void setLocalFileDataData(final byte[] data) { 382 localData = ZipUtil.copy(data); 383 } 384 }