1 package org.bouncycastle.jcajce.provider.asymmetric.ec; 2 3 import java.io.ByteArrayOutputStream; 4 import java.security.AlgorithmParameters; 5 import java.security.InvalidAlgorithmParameterException; 6 import java.security.InvalidKeyException; 7 import java.security.Key; 8 import java.security.NoSuchAlgorithmException; 9 import java.security.PrivateKey; 10 import java.security.PublicKey; 11 import java.security.SecureRandom; 12 import java.security.spec.AlgorithmParameterSpec; 13 14 import javax.crypto.BadPaddingException; 15 import javax.crypto.Cipher; 16 import javax.crypto.CipherSpi; 17 import javax.crypto.IllegalBlockSizeException; 18 import javax.crypto.NoSuchPaddingException; 19 import javax.crypto.ShortBufferException; 20 21 import org.bouncycastle.crypto.CryptoServicesRegistrar; 22 import org.bouncycastle.crypto.digests.Blake2bDigest; 23 import org.bouncycastle.crypto.digests.Blake2sDigest; 24 import org.bouncycastle.crypto.digests.MD5Digest; 25 import org.bouncycastle.crypto.digests.RIPEMD160Digest; 26 import org.bouncycastle.crypto.digests.SHA1Digest; 27 import org.bouncycastle.crypto.digests.SHA224Digest; 28 import org.bouncycastle.crypto.digests.SHA256Digest; 29 import org.bouncycastle.crypto.digests.SHA384Digest; 30 import org.bouncycastle.crypto.digests.SHA512Digest; 31 import org.bouncycastle.crypto.digests.WhirlpoolDigest; 32 import org.bouncycastle.crypto.engines.SM2Engine; 33 import org.bouncycastle.crypto.params.AsymmetricKeyParameter; 34 import org.bouncycastle.crypto.params.ParametersWithRandom; 35 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; 36 import org.bouncycastle.jcajce.provider.util.BadBlockException; 37 import org.bouncycastle.jcajce.util.BCJcaJceHelper; 38 import org.bouncycastle.jcajce.util.JcaJceHelper; 39 import org.bouncycastle.jce.interfaces.ECKey; 40 import org.bouncycastle.util.Arrays; 41 import org.bouncycastle.util.Strings; 42 43 44 public class GMCipherSpi 45 extends CipherSpi 46 { 47 private final JcaJceHelper helper = new BCJcaJceHelper(); 48 49 private SM2Engine engine; 50 private int state = -1; 51 private ErasableOutputStream buffer = new ErasableOutputStream(); 52 private AsymmetricKeyParameter key; 53 private SecureRandom random; 54 GMCipherSpi(SM2Engine engine)55 public GMCipherSpi(SM2Engine engine) 56 { 57 this.engine = engine; 58 } 59 engineGetBlockSize()60 public int engineGetBlockSize() 61 { 62 return 0; 63 } 64 engineGetKeySize(Key key)65 public int engineGetKeySize(Key key) 66 { 67 if (key instanceof ECKey) 68 { 69 return ((ECKey)key).getParameters().getCurve().getFieldSize(); 70 } 71 else 72 { 73 throw new IllegalArgumentException("not an EC key"); 74 } 75 } 76 77 engineGetIV()78 public byte[] engineGetIV() 79 { 80 return null; 81 } 82 engineGetParameters()83 public AlgorithmParameters engineGetParameters() 84 { 85 return null; 86 } 87 engineSetMode(String mode)88 public void engineSetMode(String mode) 89 throws NoSuchAlgorithmException 90 { 91 String modeName = Strings.toUpperCase(mode); 92 93 if (!modeName.equals("NONE")) 94 { 95 throw new IllegalArgumentException("can't support mode " + mode); 96 } 97 } 98 engineGetOutputSize(int inputLen)99 public int engineGetOutputSize(int inputLen) 100 { 101 if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE) 102 { 103 return engine.getOutputSize(inputLen); 104 } 105 else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE) 106 { 107 return engine.getOutputSize(inputLen); 108 } 109 else 110 { 111 throw new IllegalStateException("cipher not initialised"); 112 } 113 } 114 engineSetPadding(String padding)115 public void engineSetPadding(String padding) 116 throws NoSuchPaddingException 117 { 118 String paddingName = Strings.toUpperCase(padding); 119 120 // TDOD: make this meaningful... 121 if (!paddingName.equals("NOPADDING")) 122 { 123 throw new NoSuchPaddingException("padding not available with IESCipher"); 124 } 125 } 126 127 128 // Initialisation methods 129 engineInit( int opmode, Key key, AlgorithmParameters params, SecureRandom random)130 public void engineInit( 131 int opmode, 132 Key key, 133 AlgorithmParameters params, 134 SecureRandom random) 135 throws InvalidKeyException, InvalidAlgorithmParameterException 136 { 137 AlgorithmParameterSpec paramSpec = null; 138 139 if (params != null) 140 { 141 throw new InvalidAlgorithmParameterException("cannot recognise parameters: " + params.getClass().getName()); 142 } 143 144 engineInit(opmode, key, paramSpec, random); 145 } 146 engineInit( int opmode, Key key, AlgorithmParameterSpec engineSpec, SecureRandom random)147 public void engineInit( 148 int opmode, 149 Key key, 150 AlgorithmParameterSpec engineSpec, 151 SecureRandom random) 152 throws InvalidAlgorithmParameterException, InvalidKeyException 153 { 154 // Parse the recipient's key 155 if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) 156 { 157 if (key instanceof PublicKey) 158 { 159 this.key = ECUtils.generatePublicKeyParameter((PublicKey)key); 160 } 161 else 162 { 163 throw new InvalidKeyException("must be passed public EC key for encryption"); 164 } 165 } 166 else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) 167 { 168 if (key instanceof PrivateKey) 169 { 170 this.key = ECUtil.generatePrivateKeyParameter((PrivateKey)key); 171 } 172 else 173 { 174 throw new InvalidKeyException("must be passed private EC key for decryption"); 175 } 176 } 177 else 178 { 179 throw new InvalidKeyException("must be passed EC key"); 180 } 181 182 183 if (random != null) 184 { 185 this.random = random; 186 } 187 else 188 { 189 this.random = CryptoServicesRegistrar.getSecureRandom(); 190 } 191 192 this.state = opmode; 193 buffer.reset(); 194 } 195 engineInit( int opmode, Key key, SecureRandom random)196 public void engineInit( 197 int opmode, 198 Key key, 199 SecureRandom random) 200 throws InvalidKeyException 201 { 202 try 203 { 204 engineInit(opmode, key, (AlgorithmParameterSpec)null, random); 205 } 206 catch (InvalidAlgorithmParameterException e) 207 { 208 throw new IllegalArgumentException("cannot handle supplied parameter spec: " + e.getMessage()); 209 } 210 } 211 212 213 // Update methods - buffer the input 214 engineUpdate( byte[] input, int inputOffset, int inputLen)215 public byte[] engineUpdate( 216 byte[] input, 217 int inputOffset, 218 int inputLen) 219 { 220 buffer.write(input, inputOffset, inputLen); 221 return null; 222 } 223 224 engineUpdate( byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)225 public int engineUpdate( 226 byte[] input, 227 int inputOffset, 228 int inputLen, 229 byte[] output, 230 int outputOffset) 231 { 232 buffer.write(input, inputOffset, inputLen); 233 return 0; 234 } 235 236 237 // Finalisation methods 238 engineDoFinal( byte[] input, int inputOffset, int inputLen)239 public byte[] engineDoFinal( 240 byte[] input, 241 int inputOffset, 242 int inputLen) 243 throws IllegalBlockSizeException, BadPaddingException 244 { 245 if (inputLen != 0) 246 { 247 buffer.write(input, inputOffset, inputLen); 248 } 249 250 try 251 { 252 if (state == Cipher.ENCRYPT_MODE || state == Cipher.WRAP_MODE) 253 { 254 // Encrypt the buffer 255 try 256 { 257 engine.init(true, new ParametersWithRandom(key, random)); 258 259 return engine.processBlock(buffer.getBuf(), 0, buffer.size()); 260 } 261 catch (final Exception e) 262 { 263 throw new BadBlockException("unable to process block", e); 264 } 265 } 266 else if (state == Cipher.DECRYPT_MODE || state == Cipher.UNWRAP_MODE) 267 { 268 // Decrypt the buffer 269 try 270 { 271 engine.init(false, key); 272 273 return engine.processBlock(buffer.getBuf(), 0, buffer.size()); 274 } 275 catch (final Exception e) 276 { 277 throw new BadBlockException("unable to process block", e); 278 } 279 } 280 else 281 { 282 throw new IllegalStateException("cipher not initialised"); 283 } 284 } 285 finally 286 { 287 buffer.erase(); 288 } 289 } 290 engineDoFinal( byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)291 public int engineDoFinal( 292 byte[] input, 293 int inputOffset, 294 int inputLength, 295 byte[] output, 296 int outputOffset) 297 throws ShortBufferException, IllegalBlockSizeException, BadPaddingException 298 { 299 byte[] buf = engineDoFinal(input, inputOffset, inputLength); 300 System.arraycopy(buf, 0, output, outputOffset, buf.length); 301 return buf.length; 302 } 303 304 /** 305 * Classes that inherit from us 306 */ 307 static public class SM2 308 extends GMCipherSpi 309 { SM2()310 public SM2() 311 { 312 super(new SM2Engine()); 313 } 314 } 315 316 static public class SM2withBlake2b 317 extends GMCipherSpi 318 { SM2withBlake2b()319 public SM2withBlake2b() 320 { 321 super(new SM2Engine(new Blake2bDigest(512))); 322 } 323 } 324 325 static public class SM2withBlake2s 326 extends GMCipherSpi 327 { SM2withBlake2s()328 public SM2withBlake2s() 329 { 330 super(new SM2Engine(new Blake2sDigest(256))); 331 } 332 } 333 334 static public class SM2withWhirlpool 335 extends GMCipherSpi 336 { SM2withWhirlpool()337 public SM2withWhirlpool() 338 { 339 super(new SM2Engine(new WhirlpoolDigest())); 340 } 341 } 342 343 static public class SM2withMD5 344 extends GMCipherSpi 345 { SM2withMD5()346 public SM2withMD5() 347 { 348 super(new SM2Engine(new MD5Digest())); 349 } 350 } 351 352 static public class SM2withRMD 353 extends GMCipherSpi 354 { SM2withRMD()355 public SM2withRMD() 356 { 357 super(new SM2Engine(new RIPEMD160Digest())); 358 } 359 } 360 361 static public class SM2withSha1 362 extends GMCipherSpi 363 { SM2withSha1()364 public SM2withSha1() 365 { 366 super(new SM2Engine(new SHA1Digest())); 367 } 368 } 369 370 static public class SM2withSha224 371 extends GMCipherSpi 372 { SM2withSha224()373 public SM2withSha224() 374 { 375 super(new SM2Engine(new SHA224Digest())); 376 } 377 } 378 379 static public class SM2withSha256 380 extends GMCipherSpi 381 { SM2withSha256()382 public SM2withSha256() 383 { 384 super(new SM2Engine(new SHA256Digest())); 385 } 386 } 387 388 static public class SM2withSha384 389 extends GMCipherSpi 390 { SM2withSha384()391 public SM2withSha384() 392 { 393 super(new SM2Engine(new SHA384Digest())); 394 } 395 } 396 397 static public class SM2withSha512 398 extends GMCipherSpi 399 { SM2withSha512()400 public SM2withSha512() 401 { 402 super(new SM2Engine(new SHA512Digest())); 403 } 404 } 405 406 protected static final class ErasableOutputStream 407 extends ByteArrayOutputStream 408 { ErasableOutputStream()409 public ErasableOutputStream() 410 { 411 } 412 getBuf()413 public byte[] getBuf() 414 { 415 return buf; 416 } 417 erase()418 public void erase() 419 { 420 Arrays.fill(this.buf, (byte)0); 421 reset(); 422 } 423 } 424 } 425