1 package org.bouncycastle.crypto.encodings; 2 3 import org.bouncycastle.crypto.AsymmetricBlockCipher; 4 import org.bouncycastle.crypto.CipherParameters; 5 import org.bouncycastle.crypto.InvalidCipherTextException; 6 import org.bouncycastle.crypto.params.ParametersWithRandom; 7 import org.bouncycastle.crypto.params.RSAKeyParameters; 8 9 /** 10 * ISO 9796-1 padding. Note in the light of recent results you should 11 * only use this with RSA (rather than the "simpler" Rabin keys) and you 12 * should never use it with anything other than a hash (ie. even if the 13 * message is small don't sign the message, sign it's hash) or some "random" 14 * value. See your favorite search engine for details. 15 */ 16 public class ISO9796d1Encoding 17 implements AsymmetricBlockCipher 18 { 19 private static byte[] shadows = { 0xe, 0x3, 0x5, 0x8, 0x9, 0x4, 0x2, 0xf, 20 0x0, 0xd, 0xb, 0x6, 0x7, 0xa, 0xc, 0x1 }; 21 private static byte[] inverse = { 0x8, 0xf, 0x6, 0x1, 0x5, 0x2, 0xb, 0xc, 22 0x3, 0x4, 0xd, 0xa, 0xe, 0x9, 0x0, 0x7 }; 23 24 private AsymmetricBlockCipher engine; 25 private boolean forEncryption; 26 private int bitSize; 27 private int padBits = 0; 28 29 public ISO9796d1Encoding( 30 AsymmetricBlockCipher cipher) 31 { 32 this.engine = cipher; 33 } 34 35 public AsymmetricBlockCipher getUnderlyingCipher() 36 { 37 return engine; 38 } 39 40 public void init( 41 boolean forEncryption, 42 CipherParameters param) 43 { 44 RSAKeyParameters kParam = null; 45 46 if (param instanceof ParametersWithRandom) 47 { 48 ParametersWithRandom rParam = (ParametersWithRandom)param; 49 50 kParam = (RSAKeyParameters)rParam.getParameters(); 51 } 52 else 53 { 54 kParam = (RSAKeyParameters)param; 55 } 56 57 engine.init(forEncryption, param); 58 59 bitSize = kParam.getModulus().bitLength(); 60 61 this.forEncryption = forEncryption; 62 } 63 64 /** 65 * return the input block size. The largest message we can process 66 * is (key_size_in_bits + 3)/16, which in our world comes to 67 * key_size_in_bytes / 2. 68 */ 69 public int getInputBlockSize() 70 { 71 int baseBlockSize = engine.getInputBlockSize(); 72 73 if (forEncryption) 74 { 75 return (baseBlockSize + 1) / 2; 76 } 77 else 78 { 79 return baseBlockSize; 80 } 81 } 82 83 /** 84 * return the maximum possible size for the output. 85 */ 86 public int getOutputBlockSize() 87 { 88 int baseBlockSize = engine.getOutputBlockSize(); 89 90 if (forEncryption) 91 { 92 return baseBlockSize; 93 } 94 else 95 { 96 return (baseBlockSize + 1) / 2; 97 } 98 } 99 100 /** 101 * set the number of bits in the next message to be treated as 102 * pad bits. 103 */ 104 public void setPadBits( 105 int padBits) 106 { 107 if (padBits > 7) 108 { 109 throw new IllegalArgumentException("padBits > 7"); 110 } 111 112 this.padBits = padBits; 113 } 114 115 /** 116 * retrieve the number of pad bits in the last decoded message. 117 */ 118 public int getPadBits() 119 { 120 return padBits; 121 } 122 123 public byte[] processBlock( 124 byte[] in, 125 int inOff, 126 int inLen) 127 throws InvalidCipherTextException 128 { 129 if (forEncryption) 130 { 131 return encodeBlock(in, inOff, inLen); 132 } 133 else 134 { 135 return decodeBlock(in, inOff, inLen); 136 } 137 } 138 139 private byte[] encodeBlock( 140 byte[] in, 141 int inOff, 142 int inLen) 143 throws InvalidCipherTextException 144 { 145 byte[] block = new byte[(bitSize + 7) / 8]; 146 int r = padBits + 1; 147 int z = inLen; 148 int t = (bitSize + 13) / 16; 149 150 for (int i = 0; i < t; i += z) 151 { 152 if (i > t - z) 153 { 154 System.arraycopy(in, inOff + inLen - (t - i), 155 block, block.length - t, t - i); 156 } 157 else 158 { 159 System.arraycopy(in, inOff, block, block.length - (i + z), z); 160 } 161 } 162 163 for (int i = block.length - 2 * t; i != block.length; i += 2) 164 { 165 byte val = block[block.length - t + i / 2]; 166 167 block[i] = (byte)((shadows[(val & 0xff) >>> 4] << 4) 168 | shadows[val & 0x0f]); 169 block[i + 1] = val; 170 } 171 172 block[block.length - 2 * z] ^= r; 173 block[block.length - 1] = (byte)((block[block.length - 1] << 4) | 0x06); 174 175 int maxBit = (8 - (bitSize - 1) % 8); 176 int offSet = 0; 177 178 if (maxBit != 8) 179 { 180 block[0] &= 0xff >>> maxBit; 181 block[0] |= 0x80 >>> maxBit; 182 } 183 else 184 { 185 block[0] = 0x00; 186 block[1] |= 0x80; 187 offSet = 1; 188 } 189 190 return engine.processBlock(block, offSet, block.length - offSet); 191 } 192 193 /** 194 * @exception InvalidCipherTextException if the decrypted block is not a valid ISO 9796 bit string 195 */ 196 private byte[] decodeBlock( 197 byte[] in, 198 int inOff, 199 int inLen) 200 throws InvalidCipherTextException 201 { 202 byte[] block = engine.processBlock(in, inOff, inLen); 203 int r = 1; 204 int t = (bitSize + 13) / 16; 205 206 if ((block[block.length - 1] & 0x0f) != 0x6) 207 { 208 throw new InvalidCipherTextException("invalid forcing byte in block"); 209 } 210 211 block[block.length - 1] = (byte)(((block[block.length - 1] & 0xff) >>> 4) | ((inverse[(block[block.length - 2] & 0xff) >> 4]) << 4)); 212 block[0] = (byte)((shadows[(block[1] & 0xff) >>> 4] << 4) 213 | shadows[block[1] & 0x0f]); 214 215 boolean boundaryFound = false; 216 int boundary = 0; 217 218 for (int i = block.length - 1; i >= block.length - 2 * t; i -= 2) 219 { 220 int val = ((shadows[(block[i] & 0xff) >>> 4] << 4) 221 | shadows[block[i] & 0x0f]); 222 223 if (((block[i - 1] ^ val) & 0xff) != 0) 224 { 225 if (!boundaryFound) 226 { 227 boundaryFound = true; 228 r = (block[i - 1] ^ val) & 0xff; 229 boundary = i - 1; 230 } 231 else 232 { 233 throw new InvalidCipherTextException("invalid tsums in block"); 234 } 235 } 236 } 237 238 block[boundary] = 0; 239 240 byte[] nblock = new byte[(block.length - boundary) / 2]; 241 242 for (int i = 0; i < nblock.length; i++) 243 { 244 nblock[i] = block[2 * i + boundary + 1]; 245 } 246 247 padBits = r - 1; 248 249 return nblock; 250 } 251 } 252