/* Copyright 2014 Duncan Jones * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.cryptonode.jncryptor; class AES256v2HeaderData { private static final int SIZE_WITH_PASSWORD = AES256v3Ciphertext.HEADER_SIZE + AES256v3Ciphertext.ENCRYPTION_SALT_LENGTH + AES256v3Ciphertext.HMAC_SALT_LENGTH + AES256v3Ciphertext.AES_BLOCK_SIZE; private static final int SIZE_WITHOUT_PASSWORD = AES256v3Ciphertext.HEADER_SIZE + AES256v3Ciphertext.AES_BLOCK_SIZE; private final byte version; private final byte options; private final byte[] encryptionSalt; private final byte[] hmacSalt; private final byte[] iv; private final boolean isPasswordBased; /** * Parses the header data. * * @throws InvalidDataException * */ AES256v2HeaderData(byte[] data) throws InvalidDataException { Validate.notNull(data, "Data cannot be null."); // Need the header to be able to determine the length if (data.length < AES256v3Ciphertext.HEADER_SIZE) { throw new InvalidDataException("Not enough data to read header."); } int index = 0; version = data[index++]; if (version != AES256v3Ciphertext.EXPECTED_VERSION) { throw new InvalidDataException(String.format( "Expected version %d but found %d.", AES256v3Ciphertext.EXPECTED_VERSION, version)); } options = data[index++]; // Test for any invalid flags if (options != 0x00 && options != AES256v3Ciphertext.FLAG_PASSWORD) { throw new InvalidDataException("Unrecognised bit in the options byte."); } // If the password bit is set, we can expect salt values isPasswordBased = ((options & AES256v3Ciphertext.FLAG_PASSWORD) == AES256v3Ciphertext.FLAG_PASSWORD); final int minimumLength = (isPasswordBased) ? SIZE_WITH_PASSWORD : SIZE_WITHOUT_PASSWORD; if (data.length < minimumLength) { throw new InvalidDataException(String.format( "Data must be a minimum length of %d bytes, but found %d bytes.", minimumLength, data.length)); } if (isPasswordBased) { encryptionSalt = new byte[AES256v3Ciphertext.ENCRYPTION_SALT_LENGTH]; System.arraycopy(data, index, encryptionSalt, 0, encryptionSalt.length); index += encryptionSalt.length; hmacSalt = new byte[AES256v3Ciphertext.HMAC_SALT_LENGTH]; System.arraycopy(data, index, hmacSalt, 0, hmacSalt.length); index += hmacSalt.length; } else { encryptionSalt = null; hmacSalt = null; } iv = new byte[AES256v3Ciphertext.AES_BLOCK_SIZE]; System.arraycopy(data, index, iv, 0, iv.length); index += iv.length; } /** * Gets the size of the header for password-protected data. * * @return the byte length */ static int getSizeWithPassword() { return SIZE_WITH_PASSWORD; } /** * Gets the size of the header for key-protected data. * * @return the byte length */ static int getSizeWithoutPassword() { return SIZE_WITHOUT_PASSWORD; } /** * Gets the version byte. * * @return the version byte */ byte getVersion() { return version; } /** * Getes the options byte. * * @return the options byte */ byte getOptions() { return options; } /** * Returns the encryption salt. Will be <code>null</code> if the header is for * key-protected data. * * @return the encryption salt, or <code>null</code> */ byte[] getEncryptionSalt() { return encryptionSalt; } /** * Returns the HMAC salt. Will be <code>null</code> if the header is for * key-protected data. * * @return the HMAC salt, or <code>null</code> */ byte[] getHmacSalt() { return hmacSalt; } /** * The IV value. * * @return the iv */ byte[] getIv() { return iv; } /** * Indicates if the header is for password-protected data or key-protected * data. In the former case, the encryption and HMAC salt values will be * included. * * @return <code>true</code> if the header is for password-protected data, * <code>false</code> otherwise */ boolean isPasswordBased() { return isPasswordBased; } }