summary refs log tree commit diff
path: root/Crypto/src/crypto/encodings/OaepEncoding.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Crypto/src/crypto/encodings/OaepEncoding.cs')
-rw-r--r--Crypto/src/crypto/encodings/OaepEncoding.cs345
1 files changed, 345 insertions, 0 deletions
diff --git a/Crypto/src/crypto/encodings/OaepEncoding.cs b/Crypto/src/crypto/encodings/OaepEncoding.cs
new file mode 100644

index 000000000..81561e7f5 --- /dev/null +++ b/Crypto/src/crypto/encodings/OaepEncoding.cs
@@ -0,0 +1,345 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Encodings +{ + /** + * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2. + */ + public class OaepEncoding + : IAsymmetricBlockCipher + { + private byte[] defHash; + private IDigest hash; + private IDigest mgf1Hash; + + private IAsymmetricBlockCipher engine; + private SecureRandom random; + private bool forEncryption; + + public OaepEncoding( + IAsymmetricBlockCipher cipher) + : this(cipher, new Sha1Digest(), null) + { + } + + public OaepEncoding( + IAsymmetricBlockCipher cipher, + IDigest hash) + : this(cipher, hash, null) + { + } + + public OaepEncoding( + IAsymmetricBlockCipher cipher, + IDigest hash, + byte[] encodingParams) + : this(cipher, hash, hash, encodingParams) + { + } + + public OaepEncoding( + IAsymmetricBlockCipher cipher, + IDigest hash, + IDigest mgf1Hash, + byte[] encodingParams) + { + this.engine = cipher; + this.hash = hash; + this.mgf1Hash = mgf1Hash; + this.defHash = new byte[hash.GetDigestSize()]; + + if (encodingParams != null) + { + hash.BlockUpdate(encodingParams, 0, encodingParams.Length); + } + + hash.DoFinal(defHash, 0); + } + + public IAsymmetricBlockCipher GetUnderlyingCipher() + { + return engine; + } + + public string AlgorithmName + { + get { return engine.AlgorithmName + "/OAEPPadding"; } + } + + public void Init( + bool forEncryption, + ICipherParameters param) + { + if (param is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + this.random = rParam.Random; + } + 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[] inBytes, + int inOff, + int inLen) + { + if (forEncryption) + { + return encodeBlock(inBytes, inOff, inLen); + } + else + { + return decodeBlock(inBytes, inOff, inLen); + } + } + + private byte[] encodeBlock( + byte[] inBytes, + int inOff, + int inLen) + { + byte[] block = new byte[GetInputBlockSize() + 1 + 2 * defHash.Length]; + + // + // copy in the message + // + Array.Copy(inBytes, 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. + // + Array.Copy(defHash, 0, block, defHash.Length, defHash.Length); + + // + // generate the seed. + // + byte[] seed = random.GenerateSeed(defHash.Length); + + // + // 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 + // + Array.Copy(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. + */ + private byte[] decodeBlock( + byte[] inBytes, + int inOff, + int inLen) + { + byte[] data = engine.ProcessBlock(inBytes, 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()]; + + Array.Copy(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. + // + for (int i = 0; i != defHash.Length; i++) + { + if (defHash[i] != block[defHash.Length + i]) + { + 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]; + + Array.Copy(block, start, output, 0, output.Length); + + return output; + } + + /** + * int to octet string. + */ + private void ItoOSP( + int i, + byte[] sp) + { + sp[0] = (byte)((uint)i >> 24); + sp[1] = (byte)((uint)i >> 16); + sp[2] = (byte)((uint)i >> 8); + sp[3] = (byte)((uint)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; + + hash.Reset(); + + do + { + ItoOSP(counter, C); + + mgf1Hash.BlockUpdate(Z, zOff, zLen); + mgf1Hash.BlockUpdate(C, 0, C.Length); + mgf1Hash.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * hashBuf.Length, hashBuf.Length); + } + while (++counter < (length / hashBuf.Length)); + + if ((counter * hashBuf.Length) < length) + { + ItoOSP(counter, C); + + mgf1Hash.BlockUpdate(Z, zOff, zLen); + mgf1Hash.BlockUpdate(C, 0, C.Length); + mgf1Hash.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * hashBuf.Length, mask.Length - (counter * hashBuf.Length)); + } + + return mask; + } + } +} +