1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 package org.apache.commons.compress.archivers.sevenz; 19 20 import java.io.IOException; 21 import java.io.InputStream; 22 import java.security.GeneralSecurityException; 23 import java.security.MessageDigest; 24 import java.security.NoSuchAlgorithmException; 25 import javax.crypto.Cipher; 26 import javax.crypto.CipherInputStream; 27 import javax.crypto.SecretKey; 28 import javax.crypto.spec.IvParameterSpec; 29 import javax.crypto.spec.SecretKeySpec; 30 import org.apache.commons.compress.PasswordRequiredException; 31 32 class AES256SHA256Decoder extends CoderBase { 33 @Override decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] passwordBytes)34 InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength, 35 final Coder coder, final byte[] passwordBytes) throws IOException { 36 return new InputStream() { 37 private boolean isInitialized = false; 38 private CipherInputStream cipherInputStream = null; 39 40 private CipherInputStream init() throws IOException { 41 if (isInitialized) { 42 return cipherInputStream; 43 } 44 final int byte0 = 0xff & coder.properties[0]; 45 final int numCyclesPower = byte0 & 0x3f; 46 final int byte1 = 0xff & coder.properties[1]; 47 final int ivSize = ((byte0 >> 6) & 1) + (byte1 & 0x0f); 48 final int saltSize = ((byte0 >> 7) & 1) + (byte1 >> 4); 49 if (2 + saltSize + ivSize > coder.properties.length) { 50 throw new IOException("Salt size + IV size too long in " + archiveName); 51 } 52 final byte[] salt = new byte[saltSize]; 53 System.arraycopy(coder.properties, 2, salt, 0, saltSize); 54 final byte[] iv = new byte[16]; 55 System.arraycopy(coder.properties, 2 + saltSize, iv, 0, ivSize); 56 57 if (passwordBytes == null) { 58 throw new PasswordRequiredException(archiveName); 59 } 60 final byte[] aesKeyBytes; 61 if (numCyclesPower == 0x3f) { 62 aesKeyBytes = new byte[32]; 63 System.arraycopy(salt, 0, aesKeyBytes, 0, saltSize); 64 System.arraycopy(passwordBytes, 0, aesKeyBytes, saltSize, 65 Math.min(passwordBytes.length, aesKeyBytes.length - saltSize)); 66 } else { 67 final MessageDigest digest; 68 try { 69 digest = MessageDigest.getInstance("SHA-256"); 70 } catch (final NoSuchAlgorithmException noSuchAlgorithmException) { 71 throw new IOException("SHA-256 is unsupported by your Java implementation", 72 noSuchAlgorithmException); 73 } 74 final byte[] extra = new byte[8]; 75 for (long j = 0; j < (1L << numCyclesPower); j++) { 76 digest.update(salt); 77 digest.update(passwordBytes); 78 digest.update(extra); 79 for (int k = 0; k < extra.length; k++) { 80 ++extra[k]; 81 if (extra[k] != 0) { 82 break; 83 } 84 } 85 } 86 aesKeyBytes = digest.digest(); 87 } 88 89 final SecretKey aesKey = new SecretKeySpec(aesKeyBytes, "AES"); 90 try { 91 final Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 92 cipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(iv)); 93 cipherInputStream = new CipherInputStream(in, cipher); 94 isInitialized = true; 95 return cipherInputStream; 96 } catch (final GeneralSecurityException generalSecurityException) { 97 throw new IOException("Decryption error " + 98 "(do you have the JCE Unlimited Strength Jurisdiction Policy Files installed?)", 99 generalSecurityException); 100 } 101 } 102 103 @Override 104 public int read() throws IOException { 105 return init().read(); 106 } 107 108 @Override 109 public int read(final byte[] b, final int off, final int len) throws IOException { 110 return init().read(b, off, len); 111 } 112 113 @Override 114 public void close() { 115 } 116 }; 117 } 118 } 119