001: package org.bouncycastle.crypto.encodings;
002:
003: import org.bouncycastle.crypto.AsymmetricBlockCipher;
004: import org.bouncycastle.crypto.CipherParameters;
005: import org.bouncycastle.crypto.Digest;
006: import org.bouncycastle.crypto.InvalidCipherTextException;
007: import org.bouncycastle.crypto.digests.SHA1Digest;
008: import org.bouncycastle.crypto.params.ParametersWithRandom;
009:
010: import java.security.SecureRandom;
011:
012: /**
013: * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
014: */
015: public class OAEPEncoding implements AsymmetricBlockCipher {
016: private byte[] defHash;
017: private Digest hash;
018:
019: private AsymmetricBlockCipher engine;
020: private SecureRandom random;
021: private boolean forEncryption;
022:
023: public OAEPEncoding(AsymmetricBlockCipher cipher) {
024: this (cipher, new SHA1Digest(), null);
025: }
026:
027: public OAEPEncoding(AsymmetricBlockCipher cipher, Digest hash) {
028: this (cipher, hash, null);
029: }
030:
031: public OAEPEncoding(AsymmetricBlockCipher cipher, Digest hash,
032: byte[] encodingParams) {
033: this .engine = cipher;
034: this .hash = hash;
035: this .defHash = new byte[hash.getDigestSize()];
036:
037: if (encodingParams != null) {
038: hash.update(encodingParams, 0, encodingParams.length);
039: }
040:
041: hash.doFinal(defHash, 0);
042: }
043:
044: public AsymmetricBlockCipher getUnderlyingCipher() {
045: return engine;
046: }
047:
048: public void init(boolean forEncryption, CipherParameters param) {
049: if (param instanceof ParametersWithRandom) {
050: ParametersWithRandom rParam = (ParametersWithRandom) param;
051:
052: this .random = rParam.getRandom();
053: } else {
054: this .random = new SecureRandom();
055: }
056:
057: engine.init(forEncryption, param);
058:
059: this .forEncryption = forEncryption;
060: }
061:
062: public int getInputBlockSize() {
063: int baseBlockSize = engine.getInputBlockSize();
064:
065: if (forEncryption) {
066: return baseBlockSize - 1 - 2 * defHash.length;
067: } else {
068: return baseBlockSize;
069: }
070: }
071:
072: public int getOutputBlockSize() {
073: int baseBlockSize = engine.getOutputBlockSize();
074:
075: if (forEncryption) {
076: return baseBlockSize;
077: } else {
078: return baseBlockSize - 1 - 2 * defHash.length;
079: }
080: }
081:
082: public byte[] processBlock(byte[] in, int inOff, int inLen)
083: throws InvalidCipherTextException {
084: if (forEncryption) {
085: return encodeBlock(in, inOff, inLen);
086: } else {
087: return decodeBlock(in, inOff, inLen);
088: }
089: }
090:
091: public byte[] encodeBlock(byte[] in, int inOff, int inLen)
092: throws InvalidCipherTextException {
093: byte[] block = new byte[getInputBlockSize() + 1 + 2
094: * defHash.length];
095:
096: //
097: // copy in the message
098: //
099: System.arraycopy(in, inOff, block, block.length - inLen, inLen);
100:
101: //
102: // add sentinel
103: //
104: block[block.length - inLen - 1] = 0x01;
105:
106: //
107: // as the block is already zeroed - there's no need to add PS (the >= 0 pad of 0)
108: //
109:
110: //
111: // add the hash of the encoding params.
112: //
113: System.arraycopy(defHash, 0, block, defHash.length,
114: defHash.length);
115:
116: //
117: // generate the seed.
118: //
119: byte[] seed = new byte[defHash.length];
120:
121: random.nextBytes(seed);
122:
123: //
124: // mask the message block.
125: //
126: byte[] mask = maskGeneratorFunction1(seed, 0, seed.length,
127: block.length - defHash.length);
128:
129: for (int i = defHash.length; i != block.length; i++) {
130: block[i] ^= mask[i - defHash.length];
131: }
132:
133: //
134: // add in the seed
135: //
136: System.arraycopy(seed, 0, block, 0, defHash.length);
137:
138: //
139: // mask the seed.
140: //
141: mask = maskGeneratorFunction1(block, defHash.length,
142: block.length - defHash.length, defHash.length);
143:
144: for (int i = 0; i != defHash.length; i++) {
145: block[i] ^= mask[i];
146: }
147:
148: return engine.processBlock(block, 0, block.length);
149: }
150:
151: /**
152: * @exception InvalidCipherTextException if the decrypted block turns out to
153: * be badly formatted.
154: */
155: public byte[] decodeBlock(byte[] in, int inOff, int inLen)
156: throws InvalidCipherTextException {
157: byte[] data = engine.processBlock(in, inOff, inLen);
158: byte[] block = null;
159:
160: //
161: // as we may have zeros in our leading bytes for the block we produced
162: // on encryption, we need to make sure our decrypted block comes back
163: // the same size.
164: //
165: if (data.length < engine.getOutputBlockSize()) {
166: block = new byte[engine.getOutputBlockSize()];
167:
168: System.arraycopy(data, 0, block,
169: block.length - data.length, data.length);
170: } else {
171: block = data;
172: }
173:
174: if (block.length < (2 * defHash.length) + 1) {
175: throw new InvalidCipherTextException("data too short");
176: }
177:
178: //
179: // unmask the seed.
180: //
181: byte[] mask = maskGeneratorFunction1(block, defHash.length,
182: block.length - defHash.length, defHash.length);
183:
184: for (int i = 0; i != defHash.length; i++) {
185: block[i] ^= mask[i];
186: }
187:
188: //
189: // unmask the message block.
190: //
191: mask = maskGeneratorFunction1(block, 0, defHash.length,
192: block.length - defHash.length);
193:
194: for (int i = defHash.length; i != block.length; i++) {
195: block[i] ^= mask[i - defHash.length];
196: }
197:
198: //
199: // check the hash of the encoding params.
200: //
201: for (int i = 0; i != defHash.length; i++) {
202: if (defHash[i] != block[defHash.length + i]) {
203: throw new InvalidCipherTextException("data hash wrong");
204: }
205: }
206:
207: //
208: // find the data block
209: //
210: int start;
211:
212: for (start = 2 * defHash.length; start != block.length; start++) {
213: if (block[start] == 1 || block[start] != 0) {
214: break;
215: }
216: }
217:
218: if (start >= (block.length - 1) || block[start] != 1) {
219: throw new InvalidCipherTextException("data start wrong "
220: + start);
221: }
222:
223: start++;
224:
225: //
226: // extract the data block
227: //
228: byte[] output = new byte[block.length - start];
229:
230: System.arraycopy(block, start, output, 0, output.length);
231:
232: return output;
233: }
234:
235: /**
236: * int to octet string.
237: */
238: private void ItoOSP(int i, byte[] sp) {
239: sp[0] = (byte) (i >>> 24);
240: sp[1] = (byte) (i >>> 16);
241: sp[2] = (byte) (i >>> 8);
242: sp[3] = (byte) (i >>> 0);
243: }
244:
245: /**
246: * mask generator function, as described in PKCS1v2.
247: */
248: private byte[] maskGeneratorFunction1(byte[] Z, int zOff, int zLen,
249: int length) {
250: byte[] mask = new byte[length];
251: byte[] hashBuf = new byte[defHash.length];
252: byte[] C = new byte[4];
253: int counter = 0;
254:
255: hash.reset();
256:
257: do {
258: ItoOSP(counter, C);
259:
260: hash.update(Z, zOff, zLen);
261: hash.update(C, 0, C.length);
262: hash.doFinal(hashBuf, 0);
263:
264: System.arraycopy(hashBuf, 0, mask,
265: counter * defHash.length, defHash.length);
266: } while (++counter < (length / defHash.length));
267:
268: if ((counter * defHash.length) < length) {
269: ItoOSP(counter, C);
270:
271: hash.update(Z, zOff, zLen);
272: hash.update(C, 0, C.length);
273: hash.doFinal(hashBuf, 0);
274:
275: System.arraycopy(hashBuf, 0, mask,
276: counter * defHash.length, mask.length
277: - (counter * defHash.length));
278: }
279:
280: return mask;
281: }
282: }
|