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