private void initializeStream() throws IOException {
    int headerDataSize;
    if (isPasswordEncrypted) {
      headerDataSize = AES256v3Ciphertext.HEADER_SIZE
          + AES256v3Ciphertext.ENCRYPTION_SALT_LENGTH
          + AES256v3Ciphertext.HMAC_SALT_LENGTH
          + AES256v3Ciphertext.AES_BLOCK_SIZE;
    } else {
      headerDataSize = AES256v3Ciphertext.HEADER_SIZE
          + AES256v3Ciphertext.AES_BLOCK_SIZE;
    }

    byte[] headerData = new byte[headerDataSize];
    StreamUtils.readAllBytesOrFail(in, headerData); // throws EOF if insufficient data

    int offset = 0;
    byte version = headerData[offset++];

    if (version != AES256v3Ciphertext.EXPECTED_VERSION) {
      throw new IOException(String.format("Expected version %d but found %d.",
          AES256v3Ciphertext.EXPECTED_VERSION, version));
    }

    byte options = headerData[offset++];

    if (isPasswordEncrypted) {
      if (options != AES256v3Ciphertext.FLAG_PASSWORD) {
        throw new IOException("Expected password flag missing.");
      }

      byte[] decryptionSalt = new byte[AES256v3Ciphertext.ENCRYPTION_SALT_LENGTH];
      System.arraycopy(headerData, offset, decryptionSalt, 0,
          decryptionSalt.length);
      offset += decryptionSalt.length;

      byte[] hmacSalt = new byte[AES256v3Ciphertext.HMAC_SALT_LENGTH];
      System.arraycopy(headerData, offset, hmacSalt, 0, hmacSalt.length);
      offset += hmacSalt.length;

      // Derive keys
      JNCryptor cryptor = new AES256JNCryptor();
      try {
        decryptionKey = cryptor.keyForPassword(password, decryptionSalt);
        hmacKey = cryptor.keyForPassword(password, hmacSalt);
      } catch (CryptorException e) {
        throw new IOException("Failed to derive keys from password.", e);
      }

    } else {
      if (options != 0) {
        throw new IOException("Expected options byte to be zero.");
      }
    }

    byte[] iv = new byte[AES256v3Ciphertext.AES_BLOCK_SIZE];
    System.arraycopy(headerData, offset, iv, 0, iv.length);

    trailerIn = new TrailerInputStream(in, AES256v3Ciphertext.HMAC_SIZE);

    try {
      Cipher decryptCipher = Cipher
          .getInstance(AES256JNCryptor.AES_CIPHER_ALGORITHM);
      decryptCipher.init(Cipher.DECRYPT_MODE, decryptionKey,
          new IvParameterSpec(iv));

      mac = Mac.getInstance(AES256JNCryptor.HMAC_ALGORITHM);
      mac.init(hmacKey);

      // MAC the header
      mac.update(headerData);

      // The decryption stream will write the non-decrypted bytes to the mac
      // stream
      pushbackInputStream = new PushbackInputStream(new CipherInputStream(
          new MacUpdateInputStream(trailerIn, mac), decryptCipher), 1);


    } catch (GeneralSecurityException e) {
      throw new IOException("Failed to initiate cipher.", e);
    }
  }