diff options
Diffstat (limited to 'bcprov/src/main/java/org/bouncycastle/crypto/encodings')
-rw-r--r-- | bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java | 52 | ||||
-rw-r--r-- | bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java | 280 |
2 files changed, 168 insertions, 164 deletions
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java index 4dbfbffd..b5505f43 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/OAEPEncoding.java @@ -4,12 +4,17 @@ import java.security.SecureRandom; import org.bouncycastle.crypto.AsymmetricBlockCipher; import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.InvalidCipherTextException; // BEGIN android-changed import org.bouncycastle.crypto.digests.AndroidDigestFactory; // END android-changed import org.bouncycastle.crypto.params.ParametersWithRandom; +// BEGIN android-removed +// import org.bouncycastle.crypto.util.DigestFactory; +// END android-remnoved +import org.bouncycastle.util.Arrays; /** * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2. @@ -28,6 +33,7 @@ public class OAEPEncoding AsymmetricBlockCipher cipher) { // BEGIN android-changed + // Was: this(cipher, DigestFactory.createSHA1(), null); this(cipher, AndroidDigestFactory.getSHA1(), null); // END android-changed } @@ -142,6 +148,11 @@ public class OAEPEncoding int inLen) throws InvalidCipherTextException { + if (inLen > getInputBlockSize()) + { + throw new DataLengthException("input data too long"); + } + byte[] block = new byte[getInputBlockSize() + 1 + 2 * defHash.length]; // @@ -210,28 +221,17 @@ public class OAEPEncoding throws InvalidCipherTextException { byte[] data = engine.processBlock(in, inOff, inLen); - byte[] block; + byte[] block = new byte[engine.getOutputBlockSize()]; // // 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; - } + System.arraycopy(data, 0, block, block.length - data.length, data.length); - if (block.length < (2 * defHash.length) + 1) - { - throw new InvalidCipherTextException("data too short"); - } + boolean shortData = (block.length < (2 * defHash.length) + 1); // // unmask the seed. @@ -268,31 +268,29 @@ public class OAEPEncoding } } - if (defHashWrong) - { - throw new InvalidCipherTextException("data hash wrong"); - } - // // find the data block // - int start; + int start = block.length; - for (start = 2 * defHash.length; start != block.length; start++) + for (int index = 2 * defHash.length; index != block.length; index++) { - if (block[start] != 0) + if (block[index] != 0 & start == block.length) { - break; + start = index; } } - if (start >= (block.length - 1) || block[start] != 1) - { - throw new InvalidCipherTextException("data start wrong " + start); - } + boolean dataStartWrong = (start > (block.length - 1) | block[start] != 1); start++; + if (defHashWrong | shortData | dataStartWrong) + { + Arrays.fill(block, (byte)0); + throw new InvalidCipherTextException("data wrong"); + } + // // extract the data block // diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java index 71ca7f7d..b36ae58c 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java @@ -9,6 +9,7 @@ import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.util.Arrays; /** * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this @@ -34,33 +35,35 @@ public class PKCS1Encoding private static final int HEADER_LENGTH = 10; - private SecureRandom random; - private AsymmetricBlockCipher engine; - private boolean forEncryption; - private boolean forPrivateKey; - private boolean useStrictLength; - private int pLen = -1; - private byte[] fallback = null; + private SecureRandom random; + private AsymmetricBlockCipher engine; + private boolean forEncryption; + private boolean forPrivateKey; + private boolean useStrictLength; + private int pLen = -1; + private byte[] fallback = null; + private byte[] blockBuffer; /** * Basic constructor. + * * @param cipher */ public PKCS1Encoding( - AsymmetricBlockCipher cipher) + AsymmetricBlockCipher cipher) { this.engine = cipher; this.useStrictLength = useStrict(); - } + } /** * Constructor for decryption with a fixed plaintext length. - * + * * @param cipher The cipher to use for cryptographic operation. - * @param pLen Length of the expected plaintext. + * @param pLen Length of the expected plaintext. */ public PKCS1Encoding( - AsymmetricBlockCipher cipher, + AsymmetricBlockCipher cipher, int pLen) { this.engine = cipher; @@ -68,27 +71,24 @@ public class PKCS1Encoding this.pLen = pLen; } - /** - * Constructor for decryption with a fixed plaintext length and a fallback - * value that is returned, if the padding is incorrect. - * - * @param cipher - * The cipher to use for cryptographic operation. - * @param fallback - * The fallback value, we don't do an arraycopy here. - */ - public PKCS1Encoding( - AsymmetricBlockCipher cipher, + /** + * Constructor for decryption with a fixed plaintext length and a fallback + * value that is returned, if the padding is incorrect. + * + * @param cipher The cipher to use for cryptographic operation. + * @param fallback The fallback value, we don't do an arraycopy here. + */ + public PKCS1Encoding( + AsymmetricBlockCipher cipher, byte[] fallback) { - this.engine = cipher; - this.useStrictLength = useStrict(); - this.fallback = fallback; - this.pLen = fallback.length; + this.engine = cipher; + this.useStrictLength = useStrict(); + this.fallback = fallback; + this.pLen = fallback.length; } - - + // // for J2ME compatibility // @@ -124,14 +124,14 @@ public class PKCS1Encoding } public void init( - boolean forEncryption, - CipherParameters param) + boolean forEncryption, + CipherParameters param) { - AsymmetricKeyParameter kParam; + AsymmetricKeyParameter kParam; if (param instanceof ParametersWithRandom) { - ParametersWithRandom rParam = (ParametersWithRandom)param; + ParametersWithRandom rParam = (ParametersWithRandom)param; this.random = rParam.getRandom(); kParam = (AsymmetricKeyParameter)rParam.getParameters(); @@ -149,11 +149,17 @@ public class PKCS1Encoding this.forPrivateKey = kParam.isPrivate(); this.forEncryption = forEncryption; + this.blockBuffer = new byte[engine.getOutputBlockSize()]; + + if (pLen > 0 && fallback == null && random == null) + { + throw new IllegalArgumentException("encoder requires random"); + } } public int getInputBlockSize() { - int baseBlockSize = engine.getInputBlockSize(); + int baseBlockSize = engine.getInputBlockSize(); if (forEncryption) { @@ -167,7 +173,7 @@ public class PKCS1Encoding public int getOutputBlockSize() { - int baseBlockSize = engine.getOutputBlockSize(); + int baseBlockSize = engine.getOutputBlockSize(); if (forEncryption) { @@ -180,9 +186,9 @@ public class PKCS1Encoding } public byte[] processBlock( - byte[] in, - int inOff, - int inLen) + byte[] in, + int inOff, + int inLen) throws InvalidCipherTextException { if (forEncryption) @@ -196,17 +202,17 @@ public class PKCS1Encoding } private byte[] encodeBlock( - byte[] in, - int inOff, - int inLen) + byte[] in, + int inOff, + int inLen) throws InvalidCipherTextException { if (inLen > getInputBlockSize()) { throw new IllegalArgumentException("input data too large"); } - - byte[] block = new byte[engine.getInputBlockSize()]; + + byte[] block = new byte[engine.getInputBlockSize()]; if (forPrivateKey) { @@ -241,62 +247,63 @@ public class PKCS1Encoding return engine.processBlock(block, 0, block.length); } - + /** * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext * for encryption. - * + * * @param encoded The Plaintext. - * @param pLen Expected length of the plaintext. + * @param pLen Expected length of the plaintext. * @return Either 0, if the encoding is correct, or -1, if it is incorrect. */ - private static int checkPkcs1Encoding(byte[] encoded, int pLen) { - int correct = 0; - /* + private static int checkPkcs1Encoding(byte[] encoded, int pLen) + { + int correct = 0; + /* * Check if the first two bytes are 0 2 */ - correct |= (encoded[0] ^ 2); + correct |= (encoded[0] ^ 2); /* * Now the padding check, check for no 0 byte in the padding */ - int plen = encoded.length - ( - pLen /* Lenght of the PMS */ - + 1 /* Final 0-byte before PMS */ - ); - - for (int i = 1; i < plen; i++) { - int tmp = encoded[i]; - tmp |= tmp >> 1; - tmp |= tmp >> 2; - tmp |= tmp >> 4; - correct |= (tmp & 1) - 1; - } + int plen = encoded.length - ( + pLen /* Lenght of the PMS */ + + 1 /* Final 0-byte before PMS */ + ); + + for (int i = 1; i < plen; i++) + { + int tmp = encoded[i]; + tmp |= tmp >> 1; + tmp |= tmp >> 2; + tmp |= tmp >> 4; + correct |= (tmp & 1) - 1; + } /* * Make sure the padding ends with a 0 byte. */ - correct |= encoded[encoded.length - (pLen +1)]; + correct |= encoded[encoded.length - (pLen + 1)]; /* * Return 0 or 1, depending on the result. */ - correct |= correct >> 1; - correct |= correct >> 2; - correct |= correct >> 4; - return ~((correct & 1) - 1); - } - + correct |= correct >> 1; + correct |= correct >> 2; + correct |= correct >> 4; + return ~((correct & 1) - 1); + } + /** * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct. - * - * @param in The encrypted block. + * + * @param in The encrypted block. * @param inOff Offset in the encrypted block. * @param inLen Length of the encrypted block. - * //@param pLen Length of the desired output. + * //@param pLen Length of the desired output. * @return The plaintext without padding, or a random value if the padding was incorrect. - * * @throws InvalidCipherTextException */ private byte[] decodeBlockOrRandom(byte[] in, int inOff, int inLen) @@ -308,7 +315,7 @@ public class PKCS1Encoding } byte[] block = engine.processBlock(in, inOff, inLen); - byte[] random = null; + byte[] random; if (this.fallback == null) { random = new byte[this.pLen]; @@ -319,30 +326,12 @@ public class PKCS1Encoding random = fallback; } - /* - * TODO: This is a potential dangerous side channel. However, you can - * fix this by changing the RSA engine in a way, that it will always - * return blocks of the same length and prepend them with 0 bytes if - * needed. - */ - if (block.length < getOutputBlockSize()) - { - throw new InvalidCipherTextException("block truncated"); - } - - /* - * TODO: Potential side channel. Fix it by making the engine always - * return blocks of the correct length. - */ - if (useStrictLength && block.length != engine.getOutputBlockSize()) - { - throw new InvalidCipherTextException("block incorrect size"); - } + byte[] data = (useStrictLength & (block.length != engine.getOutputBlockSize())) ? blockBuffer : block; /* * Check the padding. */ - int correct = PKCS1Encoding.checkPkcs1Encoding(block, this.pLen); + int correct = PKCS1Encoding.checkPkcs1Encoding(data, this.pLen); /* * Now, to a constant time constant memory copy of the decrypted value @@ -351,52 +340,55 @@ public class PKCS1Encoding byte[] result = new byte[this.pLen]; for (int i = 0; i < this.pLen; i++) { - result[i] = (byte)((block[i + (block.length - pLen)] & (~correct)) | (random[i] & correct)); + result[i] = (byte)((data[i + (data.length - pLen)] & (~correct)) | (random[i] & correct)); } + Arrays.fill(data, (byte)0); + return result; } /** - * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format. + * @throws InvalidCipherTextException if the decrypted block is not in PKCS1 format. */ private byte[] decodeBlock( - byte[] in, - int inOff, - int inLen) + byte[] in, + int inOff, + int inLen) throws InvalidCipherTextException { /* * If the length of the expected plaintext is known, we use a constant-time decryption. * If the decryption fails, we return a random value. */ - if (this.pLen != -1) + if (this.pLen != -1) { - return this.decodeBlockOrRandom(in, inOff, inLen); - } - + return this.decodeBlockOrRandom(in, inOff, inLen); + } + byte[] block = engine.processBlock(in, inOff, inLen); + boolean incorrectLength = (useStrictLength & (block.length != engine.getOutputBlockSize())); + byte[] data; if (block.length < getOutputBlockSize()) { - throw new InvalidCipherTextException("block truncated"); + data = blockBuffer; + } + else + { + data = block; } - byte type = block[0]; + byte type = data[0]; + boolean badType; if (forPrivateKey) { - if (type != 2) - { - throw new InvalidCipherTextException("unknown block type"); - } + badType = (type != 2); } else { - if (type != 1) - { - throw new InvalidCipherTextException("unknown block type"); - } + badType = (type != 1); } // BEGIN android-added if ((type == 1 && forPrivateKey) || (type == 2 && !forPrivateKey)) @@ -405,41 +397,55 @@ public class PKCS1Encoding } // END android-added - 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"); - } - } + int start = findStart(type, data); start++; // data should start at the next byte - if (start > block.length || start < HEADER_LENGTH) + if (badType | start < HEADER_LENGTH) + { + Arrays.fill(data, (byte)0); + throw new InvalidCipherTextException("block incorrect"); + } + + // if we get this far, it's likely to be a genuine encoding error + if (incorrectLength) { - throw new InvalidCipherTextException("no data in block"); + Arrays.fill(data, (byte)0); + throw new InvalidCipherTextException("block incorrect size"); } - byte[] result = new byte[block.length - start]; + byte[] result = new byte[data.length - start]; - System.arraycopy(block, start, result, 0, result.length); + System.arraycopy(data, start, result, 0, result.length); return result; } + + private int findStart(byte type, byte[] block) + throws InvalidCipherTextException + { + int start = -1; + boolean padErr = false; + + for (int i = 1; i != block.length; i++) + { + byte pad = block[i]; + + if (pad == 0 & start < 0) + { + start = i; + } + padErr |= (type == 1 & start < 0 & pad != (byte)0xff); + } + + if (padErr) + { + return -1; + } + + return start; + } } |