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.RSAKeyParameters;
012:
013: /**
014: * ISO9796-2 - mechanism using a hash function with recovery (scheme 1)
015: */
016: public class ISO9796d2Signer implements SignerWithRecovery {
017: static final public int TRAILER_IMPLICIT = 0xBC;
018: static final public int TRAILER_RIPEMD160 = 0x31CC;
019: static final public int TRAILER_RIPEMD128 = 0x32CC;
020: static final public int TRAILER_SHA1 = 0x33CC;
021:
022: private Digest digest;
023: private AsymmetricBlockCipher cipher;
024:
025: private int trailer;
026: private int keyBits;
027: private byte[] block;
028: private byte[] mBuf;
029: private int messageLength;
030: private boolean fullMessage;
031: private byte[] recoveredMessage;
032:
033: /**
034: * Generate a signer for the with either implicit or explicit trailers
035: * for ISO9796-2.
036: *
037: * @param cipher base cipher to use for signature creation/verification
038: * @param digest digest to use.
039: * @param implicit whether or not the trailer is implicit or gives the hash.
040: */
041: public ISO9796d2Signer(AsymmetricBlockCipher cipher, Digest digest,
042: boolean implicit) {
043: this .cipher = cipher;
044: this .digest = digest;
045:
046: if (implicit) {
047: trailer = TRAILER_IMPLICIT;
048: } else {
049: if (digest instanceof SHA1Digest) {
050: trailer = TRAILER_SHA1;
051: } else if (digest instanceof RIPEMD160Digest) {
052: trailer = TRAILER_RIPEMD160;
053: } else if (digest instanceof RIPEMD128Digest) {
054: trailer = TRAILER_RIPEMD128;
055: } else {
056: throw new IllegalArgumentException(
057: "no valid trailer for digest");
058: }
059: }
060: }
061:
062: /**
063: * Constructor for a signer with an explicit digest trailer.
064: *
065: * @param cipher cipher to use.
066: * @param digest digest to sign with.
067: */
068: public ISO9796d2Signer(AsymmetricBlockCipher cipher, Digest digest) {
069: this (cipher, digest, false);
070: }
071:
072: public void init(boolean forSigning, CipherParameters param) {
073: RSAKeyParameters kParam = (RSAKeyParameters) param;
074:
075: cipher.init(forSigning, kParam);
076:
077: keyBits = kParam.getModulus().bitLength();
078:
079: block = new byte[(keyBits + 7) / 8];
080:
081: if (trailer == TRAILER_IMPLICIT) {
082: mBuf = new byte[block.length - digest.getDigestSize() - 2];
083: } else {
084: mBuf = new byte[block.length - digest.getDigestSize() - 3];
085: }
086:
087: reset();
088: }
089:
090: /**
091: * compare two byte arrays.
092: */
093: private boolean isSameAs(byte[] a, byte[] b) {
094: if (messageLength > mBuf.length) {
095: if (mBuf.length > b.length) {
096: return false;
097: }
098:
099: for (int i = 0; i != mBuf.length; i++) {
100: if (a[i] != b[i]) {
101: return false;
102: }
103: }
104: } else {
105: if (messageLength != b.length) {
106: return false;
107: }
108:
109: for (int i = 0; i != b.length; i++) {
110: if (a[i] != b[i]) {
111: return false;
112: }
113: }
114: }
115:
116: return true;
117: }
118:
119: /**
120: * clear possible sensitive data
121: */
122: private void clearBlock(byte[] block) {
123: for (int i = 0; i != block.length; i++) {
124: block[i] = 0;
125: }
126: }
127:
128: /**
129: * update the internal digest with the byte b
130: */
131: public void update(byte b) {
132: digest.update(b);
133:
134: if (messageLength < mBuf.length) {
135: mBuf[messageLength] = b;
136: }
137:
138: messageLength++;
139: }
140:
141: /**
142: * update the internal digest with the byte array in
143: */
144: public void update(byte[] in, int off, int len) {
145: digest.update(in, off, len);
146:
147: if (messageLength < mBuf.length) {
148: for (int i = 0; i < len
149: && (i + messageLength) < mBuf.length; i++) {
150: mBuf[messageLength + i] = in[off + i];
151: }
152: }
153:
154: messageLength += len;
155: }
156:
157: /**
158: * reset the internal state
159: */
160: public void reset() {
161: digest.reset();
162: messageLength = 0;
163: clearBlock(mBuf);
164:
165: if (recoveredMessage != null) {
166: clearBlock(recoveredMessage);
167: }
168:
169: recoveredMessage = null;
170: fullMessage = false;
171: }
172:
173: /**
174: * generate a signature for the loaded message using the key we were
175: * initialised with.
176: */
177: public byte[] generateSignature() throws CryptoException {
178: int digSize = digest.getDigestSize();
179:
180: int t = 0;
181: int delta = 0;
182:
183: if (trailer == TRAILER_IMPLICIT) {
184: t = 8;
185: delta = block.length - digSize - 1;
186: digest.doFinal(block, delta);
187: block[block.length - 1] = (byte) TRAILER_IMPLICIT;
188: } else {
189: t = 16;
190: delta = block.length - digSize - 2;
191: digest.doFinal(block, delta);
192: block[block.length - 2] = (byte) (trailer >>> 8);
193: block[block.length - 1] = (byte) trailer;
194: }
195:
196: byte header = 0;
197: int x = (digSize + messageLength) * 8 + t + 4 - keyBits;
198:
199: if (x > 0) {
200: int mR = messageLength - ((x + 7) / 8);
201: header = 0x60;
202:
203: delta -= mR;
204:
205: System.arraycopy(mBuf, 0, block, delta, mR);
206: } else {
207: header = 0x40;
208: delta -= messageLength;
209:
210: System.arraycopy(mBuf, 0, block, delta, messageLength);
211: }
212:
213: if ((delta - 1) > 0) {
214: for (int i = delta - 1; i != 0; i--) {
215: block[i] = (byte) 0xbb;
216: }
217: block[delta - 1] ^= (byte) 0x01;
218: block[0] = (byte) 0x0b;
219: block[0] |= header;
220: } else {
221: block[0] = (byte) 0x0a;
222: block[0] |= header;
223: }
224:
225: byte[] b = cipher.processBlock(block, 0, block.length);
226:
227: clearBlock(mBuf);
228: clearBlock(block);
229:
230: return b;
231: }
232:
233: /**
234: * return true if the signature represents a ISO9796-2 signature
235: * for the passed in message.
236: */
237: public boolean verifySignature(byte[] signature) {
238: byte[] block = null;
239:
240: try {
241: block = cipher.processBlock(signature, 0, signature.length);
242: } catch (Exception e) {
243: return false;
244: }
245:
246: if (((block[0] & 0xC0) ^ 0x40) != 0) {
247: clearBlock(mBuf);
248: clearBlock(block);
249:
250: return false;
251: }
252:
253: if (((block[block.length - 1] & 0xF) ^ 0xC) != 0) {
254: clearBlock(mBuf);
255: clearBlock(block);
256:
257: return false;
258: }
259:
260: int delta = 0;
261:
262: if (((block[block.length - 1] & 0xFF) ^ 0xBC) == 0) {
263: delta = 1;
264: } else {
265: int sigTrail = ((block[block.length - 2] & 0xFF) << 8)
266: | (block[block.length - 1] & 0xFF);
267:
268: switch (sigTrail) {
269: case TRAILER_RIPEMD160:
270: if (!(digest instanceof RIPEMD160Digest)) {
271: throw new IllegalStateException(
272: "signer should be initialised with RIPEMD160");
273: }
274: break;
275: case TRAILER_SHA1:
276: if (!(digest instanceof SHA1Digest)) {
277: throw new IllegalStateException(
278: "signer should be initialised with SHA1");
279: }
280: break;
281: case TRAILER_RIPEMD128:
282: if (!(digest instanceof RIPEMD128Digest)) {
283: throw new IllegalStateException(
284: "signer should be initialised with RIPEMD128");
285: }
286: break;
287: default:
288: throw new IllegalArgumentException(
289: "unrecognised hash in signature");
290: }
291:
292: delta = 2;
293: }
294:
295: //
296: // find out how much padding we've got
297: //
298: int mStart = 0;
299:
300: for (mStart = 0; mStart != block.length; mStart++) {
301: if (((block[mStart] & 0x0f) ^ 0x0a) == 0) {
302: break;
303: }
304: }
305:
306: mStart++;
307:
308: //
309: // check the hashes
310: //
311: byte[] hash = new byte[digest.getDigestSize()];
312:
313: int off = block.length - delta - hash.length;
314:
315: //
316: // there must be at least one byte of message string
317: //
318: if ((off - mStart) <= 0) {
319: clearBlock(mBuf);
320: clearBlock(block);
321:
322: return false;
323: }
324:
325: //
326: // if we contain the whole message as well, check the hash of that.
327: //
328: if ((block[0] & 0x20) == 0) {
329: fullMessage = true;
330:
331: digest.reset();
332: digest.update(block, mStart, off - mStart);
333: digest.doFinal(hash, 0);
334:
335: for (int i = 0; i != hash.length; i++) {
336: block[off + i] ^= hash[i];
337: if (block[off + i] != 0) {
338: clearBlock(mBuf);
339: clearBlock(block);
340:
341: return false;
342: }
343: }
344:
345: recoveredMessage = new byte[off - mStart];
346: System.arraycopy(block, mStart, recoveredMessage, 0,
347: recoveredMessage.length);
348: } else {
349: fullMessage = false;
350:
351: digest.doFinal(hash, 0);
352:
353: for (int i = 0; i != hash.length; i++) {
354: block[off + i] ^= hash[i];
355: if (block[off + i] != 0) {
356: clearBlock(mBuf);
357: clearBlock(block);
358:
359: return false;
360: }
361: }
362:
363: recoveredMessage = new byte[off - mStart];
364: System.arraycopy(block, mStart, recoveredMessage, 0,
365: recoveredMessage.length);
366: }
367:
368: //
369: // if they've input a message check what we've recovered against
370: // what was input.
371: //
372: if (messageLength != 0) {
373: if (!isSameAs(mBuf, recoveredMessage)) {
374: clearBlock(mBuf);
375: clearBlock(block);
376:
377: return false;
378: }
379: }
380:
381: clearBlock(mBuf);
382: clearBlock(block);
383:
384: return true;
385: }
386:
387: /**
388: * Return true if the full message was recoveredMessage.
389: *
390: * @return true on full message recovery, false otherwise.
391: * @see org.bouncycastle.crypto.SignerWithRecovery#hasFullMessage()
392: */
393: public boolean hasFullMessage() {
394: return fullMessage;
395: }
396:
397: /**
398: * Return a reference to the recoveredMessage message.
399: *
400: * @return the full/partial recoveredMessage message.
401: * @see org.bouncycastle.crypto.SignerWithRecovery#getRecoveredMessage()
402: */
403: public byte[] getRecoveredMessage() {
404: return recoveredMessage;
405: }
406: }
|