001: package org.bouncycastle.crypto.signers;
002:
003: import org.bouncycastle.crypto.AsymmetricBlockCipher;
004: import org.bouncycastle.crypto.CipherParameters;
005: import org.bouncycastle.crypto.CryptoException;
006: import org.bouncycastle.crypto.Digest;
007: import org.bouncycastle.crypto.SignerWithRecovery;
008: import org.bouncycastle.crypto.digests.RIPEMD128Digest;
009: import org.bouncycastle.crypto.digests.RIPEMD160Digest;
010: import org.bouncycastle.crypto.digests.SHA1Digest;
011: import org.bouncycastle.crypto.params.ParametersWithRandom;
012: import org.bouncycastle.crypto.params.ParametersWithSalt;
013: import org.bouncycastle.crypto.params.RSAKeyParameters;
014:
015: import java.security.SecureRandom;
016:
017: /**
018: * ISO9796-2 - mechanism using a hash function with recovery (scheme 2 and 3).
019: * <p>
020: * Note: the usual length for the salt is the length of the hash
021: * function used in bytes.
022: */
023: public class ISO9796d2PSSSigner implements SignerWithRecovery {
024: static final public int TRAILER_IMPLICIT = 0xBC;
025: static final public int TRAILER_RIPEMD160 = 0x31CC;
026: static final public int TRAILER_RIPEMD128 = 0x32CC;
027: static final public int TRAILER_SHA1 = 0x33CC;
028:
029: private Digest digest;
030: private AsymmetricBlockCipher cipher;
031:
032: private SecureRandom random;
033: private byte[] standardSalt;
034:
035: private int hLen;
036: private int trailer;
037: private int keyBits;
038: private byte[] block;
039: private byte[] mBuf;
040: private int messageLength;
041: private int saltLength;
042: private boolean fullMessage;
043: private byte[] recoveredMessage;
044:
045: /**
046: * Generate a signer for the with either implicit or explicit trailers
047: * for ISO9796-2, scheme 2 or 3.
048: *
049: * @param cipher base cipher to use for signature creation/verification
050: * @param digest digest to use.
051: * @param saltLength length of salt in bytes.
052: * @param implicit whether or not the trailer is implicit or gives the hash.
053: */
054: public ISO9796d2PSSSigner(AsymmetricBlockCipher cipher,
055: Digest digest, int saltLength, boolean implicit) {
056: this .cipher = cipher;
057: this .digest = digest;
058: this .hLen = digest.getDigestSize();
059: this .saltLength = saltLength;
060:
061: if (implicit) {
062: trailer = TRAILER_IMPLICIT;
063: } else {
064: if (digest instanceof SHA1Digest) {
065: trailer = TRAILER_SHA1;
066: } else if (digest instanceof RIPEMD160Digest) {
067: trailer = TRAILER_RIPEMD160;
068: } else if (digest instanceof RIPEMD128Digest) {
069: trailer = TRAILER_RIPEMD128;
070: } else {
071: throw new IllegalArgumentException(
072: "no valid trailer for digest");
073: }
074: }
075: }
076:
077: /**
078: * Constructor for a signer with an explicit digest trailer.
079: *
080: * @param cipher cipher to use.
081: * @param digest digest to sign with.
082: * @param saltLength length of salt in bytes.
083: */
084: public ISO9796d2PSSSigner(AsymmetricBlockCipher cipher,
085: Digest digest, int saltLength) {
086: this (cipher, digest, saltLength, false);
087: }
088:
089: /**
090: * Initialise the signer.
091: *
092: * @param forSigning true if for signing, false if for verification.
093: * @param param parameters for signature generation/verification. If the
094: * parameters are for generation they should be a ParametersWithRandom,
095: * a ParametersWithSalt, or just an RSAKeyParameters object. If RSAKeyParameters
096: * are passed in a SecureRandom will be created.
097: * @exception IllegalArgumentException if wrong parameter type or a fixed
098: * salt is passed in which is the wrong length.
099: */
100: public void init(boolean forSigning, CipherParameters param) {
101: RSAKeyParameters kParam;
102: int lengthOfSalt = saltLength;
103:
104: if (param instanceof ParametersWithRandom) {
105: ParametersWithRandom p = (ParametersWithRandom) param;
106:
107: kParam = (RSAKeyParameters) p.getParameters();
108: if (forSigning) {
109: random = p.getRandom();
110: }
111: } else if (param instanceof ParametersWithSalt) {
112: ParametersWithSalt p = (ParametersWithSalt) param;
113:
114: kParam = (RSAKeyParameters) p.getParameters();
115: standardSalt = p.getSalt();
116: lengthOfSalt = standardSalt.length;
117: if (standardSalt.length != saltLength) {
118: throw new IllegalArgumentException(
119: "Fixed salt is of wrong length");
120: }
121: } else {
122: kParam = (RSAKeyParameters) param;
123: if (forSigning) {
124: random = new SecureRandom();
125: }
126: }
127:
128: cipher.init(forSigning, kParam);
129:
130: keyBits = kParam.getModulus().bitLength();
131:
132: block = new byte[(keyBits + 7) / 8];
133:
134: if (trailer == TRAILER_IMPLICIT) {
135: mBuf = new byte[block.length - digest.getDigestSize()
136: - lengthOfSalt - 1 - 1];
137: } else {
138: mBuf = new byte[block.length - digest.getDigestSize()
139: - lengthOfSalt - 1 - 2];
140: }
141:
142: reset();
143: }
144:
145: /**
146: * compare two byte arrays.
147: */
148: private boolean isSameAs(byte[] a, byte[] b) {
149: if (messageLength != b.length) {
150: return false;
151: }
152:
153: for (int i = 0; i != b.length; i++) {
154: if (a[i] != b[i]) {
155: return false;
156: }
157: }
158:
159: return true;
160: }
161:
162: /**
163: * clear possible sensitive data
164: */
165: private void clearBlock(byte[] block) {
166: for (int i = 0; i != block.length; i++) {
167: block[i] = 0;
168: }
169: }
170:
171: /**
172: * update the internal digest with the byte b
173: */
174: public void update(byte b) {
175: if (messageLength < mBuf.length) {
176: mBuf[messageLength++] = b;
177: } else {
178: digest.update(b);
179: }
180: }
181:
182: /**
183: * update the internal digest with the byte array in
184: */
185: public void update(byte[] in, int off, int len) {
186: while (len > 0 && messageLength < mBuf.length) {
187: this .update(in[off]);
188: off++;
189: len--;
190: }
191:
192: if (len > 0) {
193: digest.update(in, off, len);
194: }
195: }
196:
197: /**
198: * reset the internal state
199: */
200: public void reset() {
201: digest.reset();
202: messageLength = 0;
203: if (mBuf != null) {
204: clearBlock(mBuf);
205: }
206: if (recoveredMessage != null) {
207: clearBlock(recoveredMessage);
208: recoveredMessage = null;
209: }
210: fullMessage = false;
211: }
212:
213: /**
214: * generate a signature for the loaded message using the key we were
215: * initialised with.
216: */
217: public byte[] generateSignature() throws CryptoException {
218: int digSize = digest.getDigestSize();
219:
220: byte[] m2Hash = new byte[digSize];
221:
222: digest.doFinal(m2Hash, 0);
223:
224: byte[] C = new byte[8];
225: LtoOSP(messageLength * 8, C);
226:
227: digest.update(C, 0, C.length);
228:
229: digest.update(mBuf, 0, messageLength);
230:
231: digest.update(m2Hash, 0, m2Hash.length);
232:
233: byte[] salt;
234:
235: if (standardSalt != null) {
236: salt = standardSalt;
237: } else {
238: salt = new byte[saltLength];
239: random.nextBytes(salt);
240: }
241:
242: digest.update(salt, 0, salt.length);
243:
244: byte[] hash = new byte[digest.getDigestSize()];
245:
246: digest.doFinal(hash, 0);
247:
248: int tLength = 2;
249: if (trailer == TRAILER_IMPLICIT) {
250: tLength = 1;
251: }
252:
253: int off = block.length - messageLength - salt.length - hLen
254: - tLength - 1;
255:
256: block[off] = 0x01;
257:
258: System.arraycopy(mBuf, 0, block, off + 1, messageLength);
259: System.arraycopy(salt, 0, block, off + 1 + messageLength,
260: salt.length);
261:
262: byte[] dbMask = maskGeneratorFunction1(hash, 0, hash.length,
263: block.length - hLen - tLength);
264: for (int i = 0; i != dbMask.length; i++) {
265: block[i] ^= dbMask[i];
266: }
267:
268: System.arraycopy(hash, 0, block, block.length - hLen - tLength,
269: hLen);
270:
271: if (trailer == TRAILER_IMPLICIT) {
272: block[block.length - 1] = (byte) TRAILER_IMPLICIT;
273: } else {
274: block[block.length - 2] = (byte) (trailer >>> 8);
275: block[block.length - 1] = (byte) trailer;
276: }
277:
278: block[0] &= 0x7f;
279:
280: byte[] b = cipher.processBlock(block, 0, block.length);
281:
282: clearBlock(mBuf);
283: clearBlock(block);
284: messageLength = 0;
285:
286: return b;
287: }
288:
289: /**
290: * return true if the signature represents a ISO9796-2 signature
291: * for the passed in message.
292: */
293: public boolean verifySignature(byte[] signature) {
294: byte[] block;
295:
296: try {
297: block = cipher.processBlock(signature, 0, signature.length);
298: } catch (Exception e) {
299: return false;
300: }
301:
302: //
303: // adjust block size for leading zeroes if necessary
304: //
305: if (block.length < (keyBits + 7) / 8) {
306: byte[] tmp = new byte[(keyBits + 7) / 8];
307:
308: System.arraycopy(block, 0, tmp, tmp.length - block.length,
309: block.length);
310: clearBlock(block);
311: block = tmp;
312: }
313:
314: int tLength;
315:
316: if (((block[block.length - 1] & 0xFF) ^ 0xBC) == 0) {
317: tLength = 1;
318: } else {
319: int sigTrail = ((block[block.length - 2] & 0xFF) << 8)
320: | (block[block.length - 1] & 0xFF);
321:
322: switch (sigTrail) {
323: case TRAILER_RIPEMD160:
324: if (!(digest instanceof RIPEMD160Digest)) {
325: throw new IllegalStateException(
326: "signer should be initialised with RIPEMD160");
327: }
328: break;
329: case TRAILER_SHA1:
330: if (!(digest instanceof SHA1Digest)) {
331: throw new IllegalStateException(
332: "signer should be initialised with SHA1");
333: }
334: break;
335: case TRAILER_RIPEMD128:
336: if (!(digest instanceof RIPEMD128Digest)) {
337: throw new IllegalStateException(
338: "signer should be initialised with RIPEMD128");
339: }
340: break;
341: default:
342: throw new IllegalArgumentException(
343: "unrecognised hash in signature");
344: }
345:
346: tLength = 2;
347: }
348:
349: //
350: // calculate H(m2)
351: //
352: byte[] m2Hash = new byte[hLen];
353: digest.doFinal(m2Hash, 0);
354:
355: //
356: // remove the mask
357: //
358: byte[] dbMask = maskGeneratorFunction1(block, block.length
359: - hLen - tLength, hLen, block.length - hLen - tLength);
360: for (int i = 0; i != dbMask.length; i++) {
361: block[i] ^= dbMask[i];
362: }
363:
364: block[0] &= 0x7f;
365:
366: //
367: // find out how much padding we've got
368: //
369: int mStart = 0;
370: for (; mStart != block.length; mStart++) {
371: if (block[mStart] == 0x01) {
372: break;
373: }
374: }
375:
376: mStart++;
377:
378: if (mStart >= block.length) {
379: clearBlock(block);
380: return false;
381: }
382:
383: fullMessage = (mStart > 1);
384:
385: recoveredMessage = new byte[dbMask.length - mStart - saltLength];
386:
387: System.arraycopy(block, mStart, recoveredMessage, 0,
388: recoveredMessage.length);
389:
390: //
391: // check the hashes
392: //
393: byte[] C = new byte[8];
394: LtoOSP(recoveredMessage.length * 8, C);
395:
396: digest.update(C, 0, C.length);
397:
398: if (recoveredMessage.length != 0) {
399: digest.update(recoveredMessage, 0, recoveredMessage.length);
400: }
401:
402: digest.update(m2Hash, 0, m2Hash.length);
403:
404: // Update for the salt
405: digest.update(block, mStart + recoveredMessage.length,
406: saltLength);
407:
408: byte[] hash = new byte[digest.getDigestSize()];
409: digest.doFinal(hash, 0);
410:
411: int off = block.length - tLength - hash.length;
412:
413: for (int i = 0; i != hash.length; i++) {
414: if (hash[i] != block[off + i]) {
415: clearBlock(block);
416: clearBlock(hash);
417: clearBlock(recoveredMessage);
418: fullMessage = false;
419:
420: return false;
421: }
422: }
423:
424: clearBlock(block);
425: clearBlock(hash);
426:
427: //
428: // if they've input a message check what we've recovered against
429: // what was input.
430: //
431: if (messageLength != 0) {
432: if (!isSameAs(mBuf, recoveredMessage)) {
433: clearBlock(mBuf);
434: return false;
435: }
436:
437: messageLength = 0;
438: }
439:
440: clearBlock(mBuf);
441: return true;
442: }
443:
444: /**
445: * Return true if the full message was recoveredMessage.
446: *
447: * @return true on full message recovery, false otherwise, or if not sure.
448: * @see org.bouncycastle.crypto.SignerWithRecovery#hasFullMessage()
449: */
450: public boolean hasFullMessage() {
451: return fullMessage;
452: }
453:
454: /**
455: * Return a reference to the recoveredMessage message.
456: *
457: * @return the full/partial recoveredMessage message.
458: * @see org.bouncycastle.crypto.SignerWithRecovery#getRecoveredMessage()
459: */
460: public byte[] getRecoveredMessage() {
461: return recoveredMessage;
462: }
463:
464: /**
465: * int to octet string.
466: */
467: private void ItoOSP(int i, byte[] sp) {
468: sp[0] = (byte) (i >>> 24);
469: sp[1] = (byte) (i >>> 16);
470: sp[2] = (byte) (i >>> 8);
471: sp[3] = (byte) (i >>> 0);
472: }
473:
474: /**
475: * long to octet string.
476: */
477: private void LtoOSP(long l, byte[] sp) {
478: sp[0] = (byte) (l >>> 56);
479: sp[1] = (byte) (l >>> 48);
480: sp[2] = (byte) (l >>> 40);
481: sp[3] = (byte) (l >>> 32);
482: sp[4] = (byte) (l >>> 24);
483: sp[5] = (byte) (l >>> 16);
484: sp[6] = (byte) (l >>> 8);
485: sp[7] = (byte) (l >>> 0);
486: }
487:
488: /**
489: * mask generator function, as described in PKCS1v2.
490: */
491: private byte[] maskGeneratorFunction1(byte[] Z, int zOff, int zLen,
492: int length) {
493: byte[] mask = new byte[length];
494: byte[] hashBuf = new byte[hLen];
495: byte[] C = new byte[4];
496: int counter = 0;
497:
498: digest.reset();
499:
500: while (counter < (length / hLen)) {
501: ItoOSP(counter, C);
502:
503: digest.update(Z, zOff, zLen);
504: digest.update(C, 0, C.length);
505: digest.doFinal(hashBuf, 0);
506:
507: System.arraycopy(hashBuf, 0, mask, counter * hLen, hLen);
508:
509: counter++;
510: }
511:
512: if ((counter * hLen) < length) {
513: ItoOSP(counter, C);
514:
515: digest.update(Z, zOff, zLen);
516: digest.update(C, 0, C.length);
517: digest.doFinal(hashBuf, 0);
518:
519: System.arraycopy(hashBuf, 0, mask, counter * hLen,
520: mask.length - (counter * hLen));
521: }
522:
523: return mask;
524: }
525: }
|