Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Hook <dgh@cryptoworkshop.com>2013-05-31 11:07:45 +0400
committerDavid Hook <dgh@cryptoworkshop.com>2013-05-31 11:07:45 +0400
commit2b976f5364cfdbc37d3086019d93483c983eb80b (patch)
treecb846af3fd1d43f9c2562a1fb2d06b997ad8f229 /core/src/main/java/org/bouncycastle/crypto/encodings
parent5f714bd92fbd780d22406f4bc3681be005f6f04a (diff)
initial reshuffle
Diffstat (limited to 'core/src/main/java/org/bouncycastle/crypto/encodings')
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java287
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java357
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java257
3 files changed, 901 insertions, 0 deletions
diff --git a/core/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java b/core/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java
new file mode 100644
index 00000000..ec91e1ac
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/encodings/ISO9796d1Encoding.java
@@ -0,0 +1,287 @@
+package org.bouncycastle.crypto.encodings;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+
+/**
+ * ISO 9796-1 padding. Note in the light of recent results you should
+ * only use this with RSA (rather than the "simpler" Rabin keys) and you
+ * should never use it with anything other than a hash (ie. even if the
+ * message is small don't sign the message, sign it's hash) or some "random"
+ * value. See your favorite search engine for details.
+ */
+public class ISO9796d1Encoding
+ implements AsymmetricBlockCipher
+{
+ private static final BigInteger SIXTEEN = BigInteger.valueOf(16L);
+ private static final BigInteger SIX = BigInteger.valueOf(6L);
+
+ private static byte[] shadows = { 0xe, 0x3, 0x5, 0x8, 0x9, 0x4, 0x2, 0xf,
+ 0x0, 0xd, 0xb, 0x6, 0x7, 0xa, 0xc, 0x1 };
+ private static byte[] inverse = { 0x8, 0xf, 0x6, 0x1, 0x5, 0x2, 0xb, 0xc,
+ 0x3, 0x4, 0xd, 0xa, 0xe, 0x9, 0x0, 0x7 };
+
+ private AsymmetricBlockCipher engine;
+ private boolean forEncryption;
+ private int bitSize;
+ private int padBits = 0;
+ private BigInteger modulus;
+
+ public ISO9796d1Encoding(
+ AsymmetricBlockCipher cipher)
+ {
+ this.engine = cipher;
+ }
+
+ public AsymmetricBlockCipher getUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ RSAKeyParameters kParam = null;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ kParam = (RSAKeyParameters)rParam.getParameters();
+ }
+ else
+ {
+ kParam = (RSAKeyParameters)param;
+ }
+
+ engine.init(forEncryption, param);
+
+ modulus = kParam.getModulus();
+ bitSize = modulus.bitLength();
+
+ this.forEncryption = forEncryption;
+ }
+
+ /**
+ * return the input block size. The largest message we can process
+ * is (key_size_in_bits + 3)/16, which in our world comes to
+ * key_size_in_bytes / 2.
+ */
+ public int getInputBlockSize()
+ {
+ int baseBlockSize = engine.getInputBlockSize();
+
+ if (forEncryption)
+ {
+ return (baseBlockSize + 1) / 2;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ /**
+ * return the maximum possible size for the output.
+ */
+ public int getOutputBlockSize()
+ {
+ int baseBlockSize = engine.getOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return (baseBlockSize + 1) / 2;
+ }
+ }
+
+ /**
+ * set the number of bits in the next message to be treated as
+ * pad bits.
+ */
+ public void setPadBits(
+ int padBits)
+ {
+ if (padBits > 7)
+ {
+ throw new IllegalArgumentException("padBits > 7");
+ }
+
+ this.padBits = padBits;
+ }
+
+ /**
+ * retrieve the number of pad bits in the last decoded message.
+ */
+ public int getPadBits()
+ {
+ return padBits;
+ }
+
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forEncryption)
+ {
+ return encodeBlock(in, inOff, inLen);
+ }
+ else
+ {
+ return decodeBlock(in, inOff, inLen);
+ }
+ }
+
+ private byte[] encodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = new byte[(bitSize + 7) / 8];
+ int r = padBits + 1;
+ int z = inLen;
+ int t = (bitSize + 13) / 16;
+
+ for (int i = 0; i < t; i += z)
+ {
+ if (i > t - z)
+ {
+ System.arraycopy(in, inOff + inLen - (t - i),
+ block, block.length - t, t - i);
+ }
+ else
+ {
+ System.arraycopy(in, inOff, block, block.length - (i + z), z);
+ }
+ }
+
+ for (int i = block.length - 2 * t; i != block.length; i += 2)
+ {
+ byte val = block[block.length - t + i / 2];
+
+ block[i] = (byte)((shadows[(val & 0xff) >>> 4] << 4)
+ | shadows[val & 0x0f]);
+ block[i + 1] = val;
+ }
+
+ block[block.length - 2 * z] ^= r;
+ block[block.length - 1] = (byte)((block[block.length - 1] << 4) | 0x06);
+
+ int maxBit = (8 - (bitSize - 1) % 8);
+ int offSet = 0;
+
+ if (maxBit != 8)
+ {
+ block[0] &= 0xff >>> maxBit;
+ block[0] |= 0x80 >>> maxBit;
+ }
+ else
+ {
+ block[0] = 0x00;
+ block[1] |= 0x80;
+ offSet = 1;
+ }
+
+ return engine.processBlock(block, offSet, block.length - offSet);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block is not a valid ISO 9796 bit string
+ */
+ private byte[] decodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = engine.processBlock(in, inOff, inLen);
+ int r = 1;
+ int t = (bitSize + 13) / 16;
+
+ BigInteger iS = new BigInteger(1, block);
+ BigInteger iR;
+ if (iS.mod(SIXTEEN).equals(SIX))
+ {
+ iR = iS;
+ }
+ else if ((modulus.subtract(iS)).mod(SIXTEEN).equals(SIX))
+ {
+ iR = modulus.subtract(iS);
+ }
+ else
+ {
+ throw new InvalidCipherTextException("resulting integer iS or (modulus - iS) is not congruent to 6 mod 16");
+ }
+
+ block = convertOutputDecryptOnly(iR);
+
+ if ((block[block.length - 1] & 0x0f) != 0x6 )
+ {
+ throw new InvalidCipherTextException("invalid forcing byte in block");
+ }
+
+ block[block.length - 1] = (byte)(((block[block.length - 1] & 0xff) >>> 4) | ((inverse[(block[block.length - 2] & 0xff) >> 4]) << 4));
+ block[0] = (byte)((shadows[(block[1] & 0xff) >>> 4] << 4)
+ | shadows[block[1] & 0x0f]);
+
+ boolean boundaryFound = false;
+ int boundary = 0;
+
+ for (int i = block.length - 1; i >= block.length - 2 * t; i -= 2)
+ {
+ int val = ((shadows[(block[i] & 0xff) >>> 4] << 4)
+ | shadows[block[i] & 0x0f]);
+
+ if (((block[i - 1] ^ val) & 0xff) != 0)
+ {
+ if (!boundaryFound)
+ {
+ boundaryFound = true;
+ r = (block[i - 1] ^ val) & 0xff;
+ boundary = i - 1;
+ }
+ else
+ {
+ throw new InvalidCipherTextException("invalid tsums in block");
+ }
+ }
+ }
+
+ block[boundary] = 0;
+
+ byte[] nblock = new byte[(block.length - boundary) / 2];
+
+ for (int i = 0; i < nblock.length; i++)
+ {
+ nblock[i] = block[2 * i + boundary + 1];
+ }
+
+ padBits = r - 1;
+
+ return nblock;
+ }
+
+ private static byte[] convertOutputDecryptOnly(BigInteger result)
+ {
+ byte[] output = result.toByteArray();
+ if (output[0] == 0) // have ended up with an extra zero byte, copy down.
+ {
+ byte[] tmp = new byte[output.length - 1];
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+ return tmp;
+ }
+ return output;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java b/core/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
new file mode 100644
index 00000000..17d8e3b0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java
@@ -0,0 +1,357 @@
+package org.bouncycastle.crypto.encodings;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+/**
+ * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
+ */
+public class OAEPEncoding
+ implements AsymmetricBlockCipher
+{
+ private byte[] defHash;
+ private Digest mgf1Hash;
+
+ private AsymmetricBlockCipher engine;
+ private SecureRandom random;
+ private boolean forEncryption;
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher)
+ {
+ this(cipher, new SHA1Digest(), null);
+ }
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher,
+ Digest hash)
+ {
+ this(cipher, hash, null);
+ }
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher,
+ Digest hash,
+ byte[] encodingParams)
+ {
+ this(cipher, hash, hash, encodingParams);
+ }
+
+ public OAEPEncoding(
+ AsymmetricBlockCipher cipher,
+ Digest hash,
+ Digest mgf1Hash,
+ byte[] encodingParams)
+ {
+ this.engine = cipher;
+ this.mgf1Hash = mgf1Hash;
+ this.defHash = new byte[hash.getDigestSize()];
+
+ hash.reset();
+
+ if (encodingParams != null)
+ {
+ hash.update(encodingParams, 0, encodingParams.length);
+ }
+
+ hash.doFinal(defHash, 0);
+ }
+
+ public AsymmetricBlockCipher getUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ }
+
+ engine.init(forEncryption, param);
+
+ this.forEncryption = forEncryption;
+ }
+
+ public int getInputBlockSize()
+ {
+ int baseBlockSize = engine.getInputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize - 1 - 2 * defHash.length;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ public int getOutputBlockSize()
+ {
+ int baseBlockSize = engine.getOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return baseBlockSize - 1 - 2 * defHash.length;
+ }
+ }
+
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forEncryption)
+ {
+ return encodeBlock(in, inOff, inLen);
+ }
+ else
+ {
+ return decodeBlock(in, inOff, inLen);
+ }
+ }
+
+ public byte[] encodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = new byte[getInputBlockSize() + 1 + 2 * defHash.length];
+
+ //
+ // copy in the message
+ //
+ System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+
+ //
+ // add sentinel
+ //
+ block[block.length - inLen - 1] = 0x01;
+
+ //
+ // as the block is already zeroed - there's no need to add PS (the >= 0 pad of 0)
+ //
+
+ //
+ // add the hash of the encoding params.
+ //
+ System.arraycopy(defHash, 0, block, defHash.length, defHash.length);
+
+ //
+ // generate the seed.
+ //
+ byte[] seed = new byte[defHash.length];
+
+ random.nextBytes(seed);
+
+ //
+ // mask the message block.
+ //
+ byte[] mask = maskGeneratorFunction1(seed, 0, seed.length, block.length - defHash.length);
+
+ for (int i = defHash.length; i != block.length; i++)
+ {
+ block[i] ^= mask[i - defHash.length];
+ }
+
+ //
+ // add in the seed
+ //
+ System.arraycopy(seed, 0, block, 0, defHash.length);
+
+ //
+ // mask the seed.
+ //
+ mask = maskGeneratorFunction1(
+ block, defHash.length, block.length - defHash.length, defHash.length);
+
+ for (int i = 0; i != defHash.length; i++)
+ {
+ block[i] ^= mask[i];
+ }
+
+ return engine.processBlock(block, 0, block.length);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block turns out to
+ * be badly formatted.
+ */
+ public byte[] decodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] data = engine.processBlock(in, inOff, inLen);
+ byte[] block;
+
+ //
+ // as we may have zeros in our leading bytes for the block we produced
+ // on encryption, we need to make sure our decrypted block comes back
+ // the same size.
+ //
+ if (data.length < engine.getOutputBlockSize())
+ {
+ block = new byte[engine.getOutputBlockSize()];
+
+ System.arraycopy(data, 0, block, block.length - data.length, data.length);
+ }
+ else
+ {
+ block = data;
+ }
+
+ if (block.length < (2 * defHash.length) + 1)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+
+ //
+ // unmask the seed.
+ //
+ byte[] mask = maskGeneratorFunction1(
+ block, defHash.length, block.length - defHash.length, defHash.length);
+
+ for (int i = 0; i != defHash.length; i++)
+ {
+ block[i] ^= mask[i];
+ }
+
+ //
+ // unmask the message block.
+ //
+ mask = maskGeneratorFunction1(block, 0, defHash.length, block.length - defHash.length);
+
+ for (int i = defHash.length; i != block.length; i++)
+ {
+ block[i] ^= mask[i - defHash.length];
+ }
+
+ //
+ // check the hash of the encoding params.
+ // long check to try to avoid this been a source of a timing attack.
+ //
+ boolean defHashWrong = false;
+
+ for (int i = 0; i != defHash.length; i++)
+ {
+ if (defHash[i] != block[defHash.length + i])
+ {
+ defHashWrong = true;
+ }
+ }
+
+ if (defHashWrong)
+ {
+ throw new InvalidCipherTextException("data hash wrong");
+ }
+
+ //
+ // find the data block
+ //
+ int start;
+
+ for (start = 2 * defHash.length; start != block.length; start++)
+ {
+ if (block[start] != 0)
+ {
+ break;
+ }
+ }
+
+ if (start >= (block.length - 1) || block[start] != 1)
+ {
+ throw new InvalidCipherTextException("data start wrong " + start);
+ }
+
+ start++;
+
+ //
+ // extract the data block
+ //
+ byte[] output = new byte[block.length - start];
+
+ System.arraycopy(block, start, output, 0, output.length);
+
+ return output;
+ }
+
+ /**
+ * int to octet string.
+ */
+ private void ItoOSP(
+ int i,
+ byte[] sp)
+ {
+ sp[0] = (byte)(i >>> 24);
+ sp[1] = (byte)(i >>> 16);
+ sp[2] = (byte)(i >>> 8);
+ sp[3] = (byte)(i >>> 0);
+ }
+
+ /**
+ * mask generator function, as described in PKCS1v2.
+ */
+ private byte[] maskGeneratorFunction1(
+ byte[] Z,
+ int zOff,
+ int zLen,
+ int length)
+ {
+ byte[] mask = new byte[length];
+ byte[] hashBuf = new byte[mgf1Hash.getDigestSize()];
+ byte[] C = new byte[4];
+ int counter = 0;
+
+ mgf1Hash.reset();
+
+ while (counter < (length / hashBuf.length))
+ {
+ ItoOSP(counter, C);
+
+ mgf1Hash.update(Z, zOff, zLen);
+ mgf1Hash.update(C, 0, C.length);
+ mgf1Hash.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, hashBuf.length);
+
+ counter++;
+ }
+
+ if ((counter * hashBuf.length) < length)
+ {
+ ItoOSP(counter, C);
+
+ mgf1Hash.update(Z, zOff, zLen);
+ mgf1Hash.update(C, 0, C.length);
+ mgf1Hash.doFinal(hashBuf, 0);
+
+ System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, mask.length - (counter * hashBuf.length));
+ }
+
+ return mask;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/core/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
new file mode 100644
index 00000000..09f1537e
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
@@ -0,0 +1,257 @@
+package org.bouncycastle.crypto.encodings;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+
+/**
+ * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
+ * depends on your application - see PKCS1 Version 2 for details.
+ */
+public class PKCS1Encoding
+ implements AsymmetricBlockCipher
+{
+ /**
+ * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to
+ * work with one of these set the system property org.bouncycastle.pkcs1.strict to false.
+ * <p>
+ * The system property is checked during construction of the encoding object, it is set to
+ * true by default.
+ * </p>
+ */
+ public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.strict";
+
+ private static final int HEADER_LENGTH = 10;
+
+ private SecureRandom random;
+ private AsymmetricBlockCipher engine;
+ private boolean forEncryption;
+ private boolean forPrivateKey;
+ private boolean useStrictLength;
+
+ /**
+ * Basic constructor.
+ * @param cipher
+ */
+ public PKCS1Encoding(
+ AsymmetricBlockCipher cipher)
+ {
+ this.engine = cipher;
+ this.useStrictLength = useStrict();
+ }
+
+ //
+ // for J2ME compatibility
+ //
+ private boolean useStrict()
+ {
+ // required if security manager has been installed.
+ String strict = (String)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY);
+ }
+ });
+
+ return strict == null || strict.equals("true");
+ }
+
+ public AsymmetricBlockCipher getUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ AsymmetricKeyParameter kParam;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ kParam = (AsymmetricKeyParameter)rParam.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ kParam = (AsymmetricKeyParameter)param;
+ }
+
+ engine.init(forEncryption, param);
+
+ this.forPrivateKey = kParam.isPrivate();
+ this.forEncryption = forEncryption;
+ }
+
+ public int getInputBlockSize()
+ {
+ int baseBlockSize = engine.getInputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize - HEADER_LENGTH;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ public int getOutputBlockSize()
+ {
+ int baseBlockSize = engine.getOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return baseBlockSize - HEADER_LENGTH;
+ }
+ }
+
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forEncryption)
+ {
+ return encodeBlock(in, inOff, inLen);
+ }
+ else
+ {
+ return decodeBlock(in, inOff, inLen);
+ }
+ }
+
+ private byte[] encodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (inLen > getInputBlockSize())
+ {
+ throw new IllegalArgumentException("input data too large");
+ }
+
+ byte[] block = new byte[engine.getInputBlockSize()];
+
+ if (forPrivateKey)
+ {
+ block[0] = 0x01; // type code 1
+
+ for (int i = 1; i != block.length - inLen - 1; i++)
+ {
+ block[i] = (byte)0xFF;
+ }
+ }
+ else
+ {
+ random.nextBytes(block); // random fill
+
+ block[0] = 0x02; // type code 2
+
+ //
+ // a zero byte marks the end of the padding, so all
+ // the pad bytes must be non-zero.
+ //
+ for (int i = 1; i != block.length - inLen - 1; i++)
+ {
+ while (block[i] == 0)
+ {
+ block[i] = (byte)random.nextInt();
+ }
+ }
+ }
+
+ block[block.length - inLen - 1] = 0x00; // mark the end of the padding
+ System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+
+ return engine.processBlock(block, 0, block.length);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format.
+ */
+ private byte[] decodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = engine.processBlock(in, inOff, inLen);
+
+ if (block.length < getOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block truncated");
+ }
+
+ byte type = block[0];
+
+ if (forPrivateKey)
+ {
+ if (type != 2)
+ {
+ throw new InvalidCipherTextException("unknown block type");
+ }
+ }
+ else
+ {
+ if (type != 1)
+ {
+ throw new InvalidCipherTextException("unknown block type");
+ }
+ }
+
+ if (useStrictLength && block.length != engine.getOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block incorrect size");
+ }
+
+ //
+ // find and extract the message block.
+ //
+ int start;
+
+ for (start = 1; start != block.length; start++)
+ {
+ byte pad = block[start];
+
+ if (pad == 0)
+ {
+ break;
+ }
+ if (type == 1 && pad != (byte)0xff)
+ {
+ throw new InvalidCipherTextException("block padding incorrect");
+ }
+ }
+
+ start++; // data should start at the next byte
+
+ if (start > block.length || start < HEADER_LENGTH)
+ {
+ throw new InvalidCipherTextException("no data in block");
+ }
+
+ byte[] result = new byte[block.length - start];
+
+ System.arraycopy(block, start, result, 0, result.length);
+
+ return result;
+ }
+}