gt:         DSAParams dsaParams = ((DSAPublicKey) pubKey).getParams();
context: /*
   * otr4j, the open source java otr library.
   *
   * Distributable under LGPL license. See terms of license at gnu.org.
   */
  package net.java.otr4j.crypto;
  
  import java.io.IOException;
  import java.math.BigInteger;
  import java.nio.ByteBuffer;
  import java.security.InvalidKeyException;
  import java.security.KeyFactory;
  import java.security.KeyPair;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.security.PrivateKey;
  import java.security.PublicKey;
  import java.security.SecureRandom;
  import java.security.interfaces.DSAKey;
  import java.security.interfaces.DSAParams;
  import java.security.interfaces.DSAPrivateKey;
  import java.security.interfaces.DSAPublicKey;
  
  import javax.crypto.KeyAgreement;
  import javax.crypto.interfaces.DHPrivateKey;
  import javax.crypto.interfaces.DHPublicKey;
  import javax.crypto.spec.DHPrivateKeySpec;
  import javax.crypto.spec.DHPublicKeySpec;
  import javax.crypto.spec.SecretKeySpec;
  
  import net.java.otr4j.io.SerializationUtils;
  
  import org.spongycastle.crypto.AsymmetricCipherKeyPair;
  import org.spongycastle.crypto.BufferedBlockCipher;
  import org.spongycastle.crypto.engines.AESFastEngine;
  import org.spongycastle.crypto.generators.DHKeyPairGenerator;
  import org.spongycastle.crypto.modes.SICBlockCipher;
  import org.spongycastle.crypto.params.DHKeyGenerationParameters;
  import org.spongycastle.crypto.params.DHParameters;
  import org.spongycastle.crypto.params.DHPrivateKeyParameters;
  import org.spongycastle.crypto.params.DHPublicKeyParameters;
  import org.spongycastle.crypto.params.DSAParameters;
  import org.spongycastle.crypto.params.DSAPrivateKeyParameters;
  import org.spongycastle.crypto.params.DSAPublicKeyParameters;
  import org.spongycastle.crypto.params.KeyParameter;
  import org.spongycastle.crypto.params.ParametersWithIV;
  import org.spongycastle.crypto.signers.DSASigner;
  import org.spongycastle.util.BigIntegers;
  
  /** @author George Politis */
  public class OtrCryptoEngineImpl implements OtrCryptoEngine {
  
      public KeyPair generateDHKeyPair() throws OtrCryptoException {
  
          // Generate a AsymmetricCipherKeyPair using BC.
          DHParameters dhParams = new DHParameters(MODULUS, GENERATOR, null,
                  DH_PRIVATE_KEY_MINIMUM_BIT_LENGTH);
          DHKeyGenerationParameters params = new DHKeyGenerationParameters(new SecureRandom(),
                  dhParams);
          DHKeyPairGenerator kpGen = new DHKeyPairGenerator();
  
          kpGen.init(params);
          AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
  
          // Convert this AsymmetricCipherKeyPair to a standard JCE KeyPair.
          DHPublicKeyParameters pub = (DHPublicKeyParameters) pair.getPublic();
          DHPrivateKeyParameters priv = (DHPrivateKeyParameters) pair.getPrivate();
  
          try {
              KeyFactory keyFac = KeyFactory.getInstance("DH");
  
              DHPublicKeySpec pubKeySpecs = new DHPublicKeySpec(pub.getY(), MODULUS, GENERATOR);
              DHPublicKey pubKey = (DHPublicKey) keyFac.generatePublic(pubKeySpecs);
  
              DHParameters dhParameters = priv.getParameters();
              DHPrivateKeySpec privKeySpecs = new DHPrivateKeySpec(priv.getX(), dhParameters.getP(),
                      dhParameters.getG());
              DHPrivateKey privKey = (DHPrivateKey) keyFac.generatePrivate(privKeySpecs);
  
              return new KeyPair(pubKey, privKey);
          } catch (Exception e) {
              throw new OtrCryptoException(e);
          }
      }
  
      public DHPublicKey getDHPublicKey(byte[] mpiBytes) throws OtrCryptoException {
          return getDHPublicKey(new BigInteger(mpiBytes));
      }
  
      public DHPublicKey getDHPublicKey(BigInteger mpi) throws OtrCryptoException {
          DHPublicKeySpec pubKeySpecs = new DHPublicKeySpec(mpi, MODULUS, GENERATOR);
          try {
              KeyFactory keyFac = KeyFactory.getInstance("DH");
              return (DHPublicKey) keyFac.generatePublic(pubKeySpecs);
          } catch (Exception e) {
              throw new OtrCryptoException(e);
          }
      }
  
      public byte[] sha256Hmac(byte[] b, byte[] key) throws OtrCryptoException {
          return this.sha256Hmac(b, key, 0);
      }
  
      public byte[] sha256Hmac(byte[] b, byte[] key, int length) throws OtrCryptoException {
  
          SecretKeySpec keyspec = new SecretKeySpec(key, "HmacSHA256");
          javax.crypto.Mac mac;
          try {
              mac = javax.crypto.Mac.getInstance("HmacSHA256");
          } catch (NoSuchAlgorithmException e) {
              throw new OtrCryptoException(e);
          }
          try {
              mac.init(keyspec);
          } catch (InvalidKeyException e) {
              throw new OtrCryptoException(e);
          }
  
          byte[] macBytes = mac.doFinal(b);
  
          if (length > 0) {
              byte[] bytes = new byte[length];
              ByteBuffer buff = ByteBuffer.wrap(macBytes);
              buff.get(bytes);
              return bytes;
          } else {
              return macBytes;
          }
      }
  
      public byte[] sha1Hmac(byte[] b, byte[] key, int length) throws OtrCryptoException {
  
          try {
              SecretKeySpec keyspec = new SecretKeySpec(key, "HmacSHA1");
              javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA1");
              mac.init(keyspec);
  
              byte[] macBytes = mac.doFinal(b);
  
              if (length > 0) {
                  byte[] bytes = new byte[length];
                  ByteBuffer buff = ByteBuffer.wrap(macBytes);
                  buff.get(bytes);
                  return bytes;
              } else {
                  return macBytes;
              }
          } catch (Exception e) {
              throw new OtrCryptoException(e);
          }
      }
  
      public byte[] sha256Hmac160(byte[] b, byte[] key) throws OtrCryptoException {
          return sha256Hmac(b, key, 20);
      }
  
      public byte[] sha256Hash(byte[] b) throws OtrCryptoException {
          try {
              MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
              sha256.update(b, 0, b.length);
              return sha256.digest();
          } catch (Exception e) {
              throw new OtrCryptoException(e);
          }
      }
  
      public byte[] sha1Hash(byte[] b) throws OtrCryptoException {
          try {
              MessageDigest sha256 = MessageDigest.getInstance("SHA-1");
              sha256.update(b, 0, b.length);
              return sha256.digest();
          } catch (Exception e) {
              throw new OtrCryptoException(e);
          }
      }
  
      public byte[] aesDecrypt(byte[] key, byte[] ctr, byte[] b) throws OtrCryptoException {
  
          AESFastEngine aesDec = new AESFastEngine();
          SICBlockCipher sicAesDec = new SICBlockCipher(aesDec);
          BufferedBlockCipher bufSicAesDec = new BufferedBlockCipher(sicAesDec);
  
          // Create initial counter value 0.
          if (ctr == null)
              ctr = ZERO_CTR;
          bufSicAesDec.init(false, new ParametersWithIV(new KeyParameter(key), ctr));
          byte[] aesOutLwDec = new byte[b.length];
          int done = bufSicAesDec.processBytes(b, 0, b.length, aesOutLwDec, 0);
          try {
              bufSicAesDec.doFinal(aesOutLwDec, done);
          } catch (Exception e) {
              throw new OtrCryptoException(e);
          }
  
          return aesOutLwDec;
      }
  
      public byte[] aesEncrypt(byte[] key, byte[] ctr, byte[] b) throws OtrCryptoException {
  
          AESFastEngine aesEnc = new AESFastEngine();
          SICBlockCipher sicAesEnc = new SICBlockCipher(aesEnc);
          BufferedBlockCipher bufSicAesEnc = new BufferedBlockCipher(sicAesEnc);
  
          // Create initial counter value 0.
          if (ctr == null)
              ctr = ZERO_CTR;
          bufSicAesEnc.init(true, new ParametersWithIV(new KeyParameter(key), ctr));
          byte[] aesOutLwEnc = new byte[b.length];
          int done = bufSicAesEnc.processBytes(b, 0, b.length, aesOutLwEnc, 0);
          try {
              bufSicAesEnc.doFinal(aesOutLwEnc, done);
          } catch (Exception e) {
              throw new OtrCryptoException(e);
          }
          return aesOutLwEnc;
      }
  
      public BigInteger generateSecret(PrivateKey privKey, PublicKey pubKey)
              throws OtrCryptoException {
          try {
              KeyAgreement ka = KeyAgreement.getInstance("DH");
              ka.init(privKey);
              ka.doPhase(pubKey, true);
              byte[] sb = ka.generateSecret();
              BigInteger s = new BigInteger(1, sb);
              return s;
  
          } catch (Exception e) {
              throw new OtrCryptoException(e);
          }
      }
  
      public byte[] sign(byte[] b, PrivateKey privatekey) throws OtrCryptoException {
  
          if (!(privatekey instanceof DSAPrivateKey))
              throw new IllegalArgumentException();
  
          DSAParams dsaParams = ((DSAPrivateKey) privatekey).getParams();
          DSAParameters bcDSAParameters = new DSAParameters(dsaParams.getP(), dsaParams.getQ(),
                  dsaParams.getG());
  
          DSAPrivateKey dsaPrivateKey = (DSAPrivateKey) privatekey;
          DSAPrivateKeyParameters bcDSAPrivateKeyParms = new DSAPrivateKeyParameters(
                  dsaPrivateKey.getX(), bcDSAParameters);
  
          DSASigner dsaSigner = new DSASigner();
          dsaSigner.init(true, bcDSAPrivateKeyParms);
  
          BigInteger q = dsaParams.getQ();
  
          // Ian: Note that if you can get the standard DSA implementation you're
          // using to not hash its input, you should be able to pass it ((256-bit
          // value) mod q), (rather than truncating the 256-bit value) and all
          // should be well.
          // ref: Interop problems with libotr - DSA signature
          BigInteger bmpi = new BigInteger(1, b);
          BigInteger[] rs = dsaSigner.generateSignature(BigIntegers.asUnsignedByteArray(bmpi.mod(q)));
  
          int siglen = q.bitLength() / 4;
          int rslen = siglen / 2;
          byte[] rb = BigIntegers.asUnsignedByteArray(rs[0]);
          byte[] sb = BigIntegers.asUnsignedByteArray(rs[1]);
  
          // Create the final signature array, padded with zeros if necessary.
          byte[] sig = new byte[siglen];
          Boolean writeR = false;
          Boolean writeS = false;
          int shiftR = rslen - rb.length;
          int shiftS = rslen - sb.length;
  
          for (int i = 0; i < siglen; i++) {
              if (i < rslen) {
                  if (!writeR)
                      writeR = rb.length >= rslen - i;
                  sig[i] = (writeR) ? rb[i - shiftR] : (byte) 0x0;
              } else {
                  int j = i - rslen; // Rebase.
                  if (!writeS)
                      writeS = sb.length >= rslen - j;
                  sig[i] = (writeS) ? sb[j - shiftS] : (byte) 0x0;
              }
          }
          return sig;
      }
  
      public boolean verify(byte[] b, PublicKey pubKey, byte[] rs) throws OtrCryptoException {
  
          if (!(pubKey instanceof DSAPublicKey))
              throw new IllegalArgumentException();
  
  