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