// Path: src/main/java/com/healthmarketscience/jackcess/crypt/util/StreamCipherCompat.java
// public interface StreamCipherCompat 
// {
//   public String getAlgorithmName();
// 
//   public void init(boolean forEncryption, CipherParameters params);
// 
//   public byte returnByte(byte in);
// 
//   public int processStreamBytes(byte[] in, int inOff,
//                                 int len, byte[] out, int outOff);
// 
//   public void reset();
// }
// 
// Path: src/main/java/com/healthmarketscience/jackcess/crypt/util/StreamCipherFactory.java
// public abstract class StreamCipherFactory
// {
//   /** compatible factory for RC4Engine instances */
//   private static final StreamCipherFactory RC4_ENGINE_FACTORY;
//   static {
//     StreamCipherFactory factory = null;
//     try {
//       // first, attempt to load a 1.51+ compatible factory instance
//       factory = loadFactory("com.healthmarketscience.jackcess.crypt.util.RC4EngineCompat$Factory");
//     } catch(Throwable t) {
//       // failed, try legacy version
//     }
// 
//     if(factory == null) {
//       try {
//         // now, attempt to load a 1.50 and earlier compatible factory instance
//         factory = loadFactory("com.healthmarketscience.jackcess.crypt.util.RC4EngineLegacy$Factory");
//       } catch(Throwable e) {
//         // sorry, no dice
//         throw new IllegalStateException("Incompatible bouncycastle version", e);
//       }
//     }
// 
//     RC4_ENGINE_FACTORY = factory;
//   }
// 
//   protected StreamCipherFactory() {}
// 
//   public static StreamCipherCompat newRC4Engine() {
//     return RC4_ENGINE_FACTORY.newInstance();
//   }
// 
//   private static StreamCipherFactory loadFactory(String className)
//     throws Exception
//   {
//     Class<?> factoryClass = Class.forName(className);
//     StreamCipherFactory factory = (StreamCipherFactory)factoryClass.newInstance();
//     // verify that the engine is functional
//     if(factory.newInstance() == null) {
//       throw new IllegalStateException("EngineFactory " + className +
//                                       " not functional");
//     }
//     return factory;
//   }
// 
//   public abstract StreamCipherCompat newInstance();
// }


import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import com.healthmarketscience.jackcess.crypt.util.StreamCipherCompat;
import com.healthmarketscience.jackcess.crypt.util.StreamCipherFactory;
import com.healthmarketscience.jackcess.impl.ByteUtil;
import com.healthmarketscience.jackcess.impl.PageChannel;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.params.KeyParameter;
  private final int _encKeyByteSize;

  public RC4CryptoAPIProvider(PageChannel channel, byte[] encodingKey,
                              ByteBuffer encProvBuf, byte[] pwdBytes)
  {
    super(channel, encodingKey);
    _header = EncryptionHeader.read(encProvBuf, VALID_CRYPTO_ALGOS,
                                    VALID_HASH_ALGOS);

    _verifier = new EncryptionVerifier(encProvBuf, _header.getCryptoAlgorithm());

    // OC: 2.3.5.2 (part 1)
    _baseHash = hash(getDigest(), _verifier.getSalt(), pwdBytes);
    _encKeyByteSize =  bits2bytes(_header.getKeySize());
  }

  @Override
  public boolean canEncodePartialPage() {
    // RC4 ciphers are not influenced by the page contents, so we can easily
    // encode part of the buffer.
    return true;
  }

  @Override
  protected Digest initDigest() {
    return new SHA1Digest();
  }

  @Override
  protected StreamCipherCompat initCipher() {
// Path: src/main/java/com/healthmarketscience/jackcess/crypt/util/StreamCipherCompat.java
// public interface StreamCipherCompat 
// {
//   public String getAlgorithmName();
// 
//   public void init(boolean forEncryption, CipherParameters params);
// 
//   public byte returnByte(byte in);
// 
//   public int processStreamBytes(byte[] in, int inOff,
//                                 int len, byte[] out, int outOff);
// 
//   public void reset();
// }
// 
// Path: src/main/java/com/healthmarketscience/jackcess/crypt/util/StreamCipherFactory.java
// public abstract class StreamCipherFactory
// {
//   /** compatible factory for RC4Engine instances */
//   private static final StreamCipherFactory RC4_ENGINE_FACTORY;
//   static {
//     StreamCipherFactory factory = null;
//     try {
//       // first, attempt to load a 1.51+ compatible factory instance
//       factory = loadFactory("com.healthmarketscience.jackcess.crypt.util.RC4EngineCompat$Factory");
//     } catch(Throwable t) {
//       // failed, try legacy version
//     }
// 
//     if(factory == null) {
//       try {
//         // now, attempt to load a 1.50 and earlier compatible factory instance
//         factory = loadFactory("com.healthmarketscience.jackcess.crypt.util.RC4EngineLegacy$Factory");
//       } catch(Throwable e) {
//         // sorry, no dice
//         throw new IllegalStateException("Incompatible bouncycastle version", e);
//       }
//     }
// 
//     RC4_ENGINE_FACTORY = factory;
//   }
// 
//   protected StreamCipherFactory() {}
// 
//   public static StreamCipherCompat newRC4Engine() {
//     return RC4_ENGINE_FACTORY.newInstance();
//   }
// 
//   private static StreamCipherFactory loadFactory(String className)
//     throws Exception
//   {
//     Class<?> factoryClass = Class.forName(className);
//     StreamCipherFactory factory = (StreamCipherFactory)factoryClass.newInstance();
//     // verify that the engine is functional
//     if(factory.newInstance() == null) {
//       throw new IllegalStateException("EngineFactory " + className +
//                                       " not functional");
//     }
//     return factory;
//   }
// 
//   public abstract StreamCipherCompat newInstance();
// }


// Path: src/main/java/com/healthmarketscience/jackcess/crypt/impl/office/RC4CryptoAPIProvider.java
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import com.healthmarketscience.jackcess.crypt.util.StreamCipherCompat;
import com.healthmarketscience.jackcess.crypt.util.StreamCipherFactory;
import com.healthmarketscience.jackcess.impl.ByteUtil;
import com.healthmarketscience.jackcess.impl.PageChannel;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.params.KeyParameter;

  private final int _encKeyByteSize;

  public RC4CryptoAPIProvider(PageChannel channel, byte[] encodingKey,
                              ByteBuffer encProvBuf, byte[] pwdBytes)
  {
    super(channel, encodingKey);
    _header = EncryptionHeader.read(encProvBuf, VALID_CRYPTO_ALGOS,
                                    VALID_HASH_ALGOS);

    _verifier = new EncryptionVerifier(encProvBuf, _header.getCryptoAlgorithm());

    // OC: 2.3.5.2 (part 1)
    _baseHash = hash(getDigest(), _verifier.getSalt(), pwdBytes);
    _encKeyByteSize =  bits2bytes(_header.getKeySize());
  }

  @Override
  public boolean canEncodePartialPage() {
    // RC4 ciphers are not influenced by the page contents, so we can easily
    // encode part of the buffer.
    return true;
  }

  @Override
  protected Digest initDigest() {
    return new SHA1Digest();
  }

  @Override
  protected StreamCipherCompat initCipher() {
    return StreamCipherFactory.newRC4Engine();