NativeCipher.java revision 13517:a043ca3a539c
1103285Sikob/*
2113584Ssimokawa * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
3113584Ssimokawa * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4103285Sikob *
5103285Sikob * This code is free software; you can redistribute it and/or modify it
6103285Sikob * under the terms of the GNU General Public License version 2 only, as
7103285Sikob * published by the Free Software Foundation.  Oracle designates this
8103285Sikob * particular file as subject to the "Classpath" exception as provided
9103285Sikob * by Oracle in the LICENSE file that accompanied this code.
10103285Sikob *
11103285Sikob * This code is distributed in the hope that it will be useful, but WITHOUT
12103285Sikob * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13103285Sikob * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14103285Sikob * version 2 for more details (a copy is included in the LICENSE file that
15103285Sikob * accompanied this code).
16103285Sikob *
17103285Sikob * You should have received a copy of the GNU General Public License version
18103285Sikob * 2 along with this work; if not, write to the Free Software Foundation,
19103285Sikob * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20103285Sikob *
21103285Sikob * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22103285Sikob * or visit www.oracle.com if you need additional information or have any
23103285Sikob * questions.
24103285Sikob */
25103285Sikob
26103285Sikobpackage com.oracle.security.ucrypto;
27103285Sikob
28103285Sikobimport java.nio.ByteBuffer;
29103285Sikobimport java.util.Set;
30103285Sikobimport java.util.Arrays;
31103285Sikobimport java.util.concurrent.ConcurrentSkipListSet;
32103285Sikobimport java.lang.ref.*;
33103285Sikob
34103285Sikobimport java.security.*;
35103285Sikobimport java.security.spec.*;
36103285Sikobimport javax.crypto.*;
37103285Sikob
38103285Sikobimport javax.crypto.spec.SecretKeySpec;
39103285Sikobimport javax.crypto.spec.IvParameterSpec;
40103285Sikob
41103285Sikobimport sun.security.jca.JCAUtil;
42103285Sikob
43103285Sikob/**
44103285Sikob * Cipher wrapper class utilizing ucrypto APIs. This class currently supports
45103285Sikob * - AES/ECB/NOPADDING
46103285Sikob * - AES/CBC/NOPADDING
47112136Ssimokawa * - AES/CTR/NOPADDING
48112136Ssimokawa * - AES/CFB128/NOPADDING
49112136Ssimokawa * (Support for GCM mode is inside the child class NativeGCMCipher)
50103285Sikob *
51103285Sikob * @since 1.9
52103285Sikob */
53103285Sikobclass NativeCipher extends CipherSpi {
54103285Sikob
55103285Sikob    // public implementation classes
56103285Sikob    public static final class AesEcbNoPadding extends NativeCipher {
57103285Sikob        public AesEcbNoPadding() throws NoSuchAlgorithmException {
58103285Sikob            super(UcryptoMech.CRYPTO_AES_ECB);
59103285Sikob        }
60103285Sikob        public AesEcbNoPadding(int keySize) throws NoSuchAlgorithmException {
61103285Sikob            super(UcryptoMech.CRYPTO_AES_ECB, keySize);
62103285Sikob        }
63103285Sikob    }
64103285Sikob    public static final class AesCbcNoPadding extends NativeCipher {
65103285Sikob        public AesCbcNoPadding() throws NoSuchAlgorithmException {
66113584Ssimokawa            super(UcryptoMech.CRYPTO_AES_CBC);
67103285Sikob        }
68103285Sikob        public AesCbcNoPadding(int keySize) throws NoSuchAlgorithmException {
69103285Sikob            super(UcryptoMech.CRYPTO_AES_CBC, keySize);
70103285Sikob        }
71103285Sikob    }
72111615Ssimokawa    public static final class AesCtrNoPadding extends NativeCipher {
73103285Sikob        public AesCtrNoPadding() throws NoSuchAlgorithmException {
74113584Ssimokawa            super(UcryptoMech.CRYPTO_AES_CTR);
75113584Ssimokawa        }
76113584Ssimokawa    }
77103285Sikob    public static final class AesCfb128NoPadding extends NativeCipher {
78113584Ssimokawa        public AesCfb128NoPadding() throws NoSuchAlgorithmException {
79103285Sikob            super(UcryptoMech.CRYPTO_AES_CFB128);
80111615Ssimokawa        }
81111615Ssimokawa    }
82111615Ssimokawa
83111615Ssimokawa    // ok as constants since AES is all we support
84111615Ssimokawa    public static final int AES_BLOCK_SIZE = 16;
85111615Ssimokawa    public static final String AES_KEY_ALGO = "AES";
86111615Ssimokawa
87111615Ssimokawa    // fields set in constructor
88111615Ssimokawa    protected final UcryptoMech mech;
89111615Ssimokawa    protected String keyAlgo;
90111615Ssimokawa    protected int blockSize;
91111615Ssimokawa    protected int fixedKeySize;
92111615Ssimokawa
93111615Ssimokawa    //
94111615Ssimokawa    // fields (re)set in every init()
95103285Sikob    //
96111615Ssimokawa    protected CipherContextRef pCtxt = null;
97103285Sikob    protected byte[] keyValue = null;
98103285Sikob    protected byte[] iv = null;
99103285Sikob    protected boolean initialized = false;
100103285Sikob    protected boolean encrypt = true;
101103285Sikob    protected int bytesBuffered = 0;
102103285Sikob
103103285Sikob    // private utility methods for key re-construction
104103285Sikob    private static final PublicKey constructPublicKey(byte[] encodedKey,
105103285Sikob                                              String encodedKeyAlgorithm)
106103285Sikob        throws InvalidKeyException, NoSuchAlgorithmException {
107103285Sikob
108103285Sikob        PublicKey key = null;
109103285Sikob        try {
110103285Sikob            KeyFactory keyFactory =
111103285Sikob                KeyFactory.getInstance(encodedKeyAlgorithm);
112103285Sikob            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
113103285Sikob            key = keyFactory.generatePublic(keySpec);
114103285Sikob        } catch (NoSuchAlgorithmException nsae) {
115103285Sikob            throw new NoSuchAlgorithmException("No provider found for " +
116103285Sikob                                               encodedKeyAlgorithm +
117103285Sikob                                               " KeyFactory");
118103285Sikob        } catch (InvalidKeySpecException ikse) {
119103285Sikob            // Should never happen
120103285Sikob            throw new InvalidKeyException("Cannot construct public key", ikse);
121103285Sikob        }
122103285Sikob        return key;
123103285Sikob    }
124103285Sikob
125111615Ssimokawa    private static final PrivateKey constructPrivateKey(byte[] encodedKey,
126103285Sikob                                                String encodedKeyAlgorithm)
127103285Sikob        throws InvalidKeyException, NoSuchAlgorithmException {
128103285Sikob
129103285Sikob        PrivateKey key = null;
130103285Sikob        try {
131103285Sikob            KeyFactory keyFactory =
132103285Sikob                KeyFactory.getInstance(encodedKeyAlgorithm);
133103285Sikob            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
134103285Sikob            key = keyFactory.generatePrivate(keySpec);
135103285Sikob        } catch (NoSuchAlgorithmException nsae) {
136103285Sikob            throw new NoSuchAlgorithmException("No provider found for " +
137103285Sikob                                               encodedKeyAlgorithm +
138103285Sikob                                               " KeyFactory");
139103285Sikob        } catch (InvalidKeySpecException ikse) {
140103285Sikob            // Should never happen
141103285Sikob            throw new InvalidKeyException("Cannot construct private key", ikse);
142103285Sikob        }
143103285Sikob        return key;
144103285Sikob    }
145103285Sikob
146103285Sikob    private static final SecretKey constructSecretKey(byte[] encodedKey,
147103285Sikob                                              String encodedKeyAlgorithm) {
148103285Sikob        return new SecretKeySpec(encodedKey, encodedKeyAlgorithm);
149103285Sikob    }
150103285Sikob
151111203Ssimokawa    // package-private utility method for general key re-construction
152103285Sikob    static final Key constructKey(int keyType, byte[] encodedKey,
153103285Sikob                                  String encodedKeyAlgorithm)
154111199Ssimokawa        throws InvalidKeyException, NoSuchAlgorithmException {
155103285Sikob        Key result = null;
156103285Sikob        switch (keyType) {
157103285Sikob        case Cipher.SECRET_KEY:
158103285Sikob            result = constructSecretKey(encodedKey,
159103285Sikob                                        encodedKeyAlgorithm);
160103285Sikob            break;
161103285Sikob        case Cipher.PRIVATE_KEY:
162103285Sikob            result = constructPrivateKey(encodedKey,
163103285Sikob                                         encodedKeyAlgorithm);
164103285Sikob            break;
165103285Sikob        case Cipher.PUBLIC_KEY:
166103285Sikob            result = constructPublicKey(encodedKey,
167103285Sikob                                        encodedKeyAlgorithm);
168103285Sikob            break;
169103285Sikob        }
170103285Sikob        return result;
171103285Sikob    }
172103285Sikob
173113584Ssimokawa    NativeCipher(UcryptoMech mech, int fixedKeySize) throws NoSuchAlgorithmException {
174113584Ssimokawa        this.mech = mech;
175113584Ssimokawa        // defaults to AES - the only supported symmetric cipher algo
176113584Ssimokawa        this.blockSize = AES_BLOCK_SIZE;
177113584Ssimokawa        this.keyAlgo = AES_KEY_ALGO;
178113584Ssimokawa        this.fixedKeySize = fixedKeySize;
179103285Sikob    }
180103285Sikob
181103285Sikob    NativeCipher(UcryptoMech mech) throws NoSuchAlgorithmException {
182113584Ssimokawa        this(mech, -1);
183103285Sikob    }
184113584Ssimokawa
185103285Sikob    @Override
186103285Sikob    protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
187113584Ssimokawa        // Disallow change of mode for now since currently it's explicitly
188103285Sikob        // defined in transformation strings
189103285Sikob        throw new NoSuchAlgorithmException("Unsupported mode " + mode);
190113584Ssimokawa    }
191103285Sikob
192103285Sikob    // see JCE spec
193113584Ssimokawa    @Override
194103285Sikob    protected void engineSetPadding(String padding)
195113584Ssimokawa            throws NoSuchPaddingException {
196103285Sikob        // Disallow change of padding for now since currently it's explicitly
197103285Sikob        // defined in transformation strings
198103285Sikob        throw new NoSuchPaddingException("Unsupported padding " + padding);
199103285Sikob    }
200103285Sikob
201103285Sikob    // see JCE spec
202103285Sikob    @Override
203103285Sikob    protected int engineGetBlockSize() {
204103285Sikob        return blockSize;
205103285Sikob    }
206103285Sikob
207113584Ssimokawa    // see JCE spec
208113584Ssimokawa    @Override
209113584Ssimokawa    protected synchronized int engineGetOutputSize(int inputLen) {
210113584Ssimokawa        return getOutputSizeByOperation(inputLen, true);
211113584Ssimokawa    }
212113584Ssimokawa
213103285Sikob    // see JCE spec
214103285Sikob    @Override
215103285Sikob    protected synchronized byte[] engineGetIV() {
216103285Sikob        return (iv != null? iv.clone() : null);
217113584Ssimokawa    }
218113584Ssimokawa
219103285Sikob    // see JCE spec
220103285Sikob    @Override
221103285Sikob    protected synchronized AlgorithmParameters engineGetParameters() {
222103285Sikob        AlgorithmParameters params = null;
223103285Sikob        try {
224103285Sikob            if (iv != null) {
225103285Sikob                IvParameterSpec ivSpec = new IvParameterSpec(iv.clone());
226113584Ssimokawa                params = AlgorithmParameters.getInstance(keyAlgo);
227113584Ssimokawa                params.init(ivSpec);
228113584Ssimokawa            }
229113584Ssimokawa        } catch (GeneralSecurityException e) {
230113584Ssimokawa            // NoSuchAlgorithmException, NoSuchProviderException
231113584Ssimokawa            // InvalidParameterSpecException
232113584Ssimokawa            throw new UcryptoException("Could not encode parameters", e);
233113584Ssimokawa        }
234113584Ssimokawa        return params;
235103285Sikob    }
236103285Sikob
237103285Sikob    @Override
238103285Sikob    protected int engineGetKeySize(Key key) throws InvalidKeyException {
239103285Sikob        return checkKey(key) * 8;
240103285Sikob    }
241103285Sikob
242113584Ssimokawa    // see JCE spec
243103285Sikob    @Override
244103285Sikob    protected synchronized void engineInit(int opmode, Key key,
245103285Sikob            SecureRandom random) throws InvalidKeyException {
246103285Sikob        try {
247113584Ssimokawa            engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
248113584Ssimokawa        } catch (InvalidAlgorithmParameterException e) {
249113584Ssimokawa            throw new InvalidKeyException("init() failed", e);
250113584Ssimokawa        }
251113584Ssimokawa    }
252103285Sikob
253103285Sikob    // see JCE spec
254113584Ssimokawa    @Override
255103285Sikob    protected synchronized void engineInit(int opmode, Key key,
256113584Ssimokawa            AlgorithmParameterSpec params, SecureRandom random)
257103285Sikob            throws InvalidKeyException, InvalidAlgorithmParameterException {
258103285Sikob        checkKey(key);
259103285Sikob        if (opmode != Cipher.ENCRYPT_MODE &&
260103285Sikob            opmode != Cipher.DECRYPT_MODE &&
261114732Ssimokawa            opmode != Cipher.WRAP_MODE &&
262110336Ssimokawa            opmode != Cipher.UNWRAP_MODE) {
263103285Sikob            throw new InvalidAlgorithmParameterException
264110336Ssimokawa                ("Unsupported mode: " + opmode);
265103285Sikob        }
266103285Sikob        boolean doEncrypt =
267103285Sikob                (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE);
268103285Sikob
269103285Sikob        byte[] ivBytes = null;
270110336Ssimokawa        if (mech == UcryptoMech.CRYPTO_AES_ECB) {
271114732Ssimokawa            if (params != null) {
272108503Ssimokawa                throw new InvalidAlgorithmParameterException
273108503Ssimokawa                        ("No Parameters for ECB mode");
274111615Ssimokawa            }
275103285Sikob        } else {
276103285Sikob            if (params != null) {
277113584Ssimokawa                if (!(params instanceof IvParameterSpec)) {
278113584Ssimokawa                    throw new InvalidAlgorithmParameterException
279111615Ssimokawa                            ("IvParameterSpec required. Received: " +
280113584Ssimokawa                            params.getClass().getName());
281103285Sikob                } else {
282113584Ssimokawa                    ivBytes = ((IvParameterSpec) params).getIV();
283103285Sikob                    if (ivBytes.length != blockSize) {
284103285Sikob                        throw new InvalidAlgorithmParameterException
285103285Sikob                             ("Wrong IV length: must be " + blockSize +
286103285Sikob                              " bytes long. Received length:" + ivBytes.length);
287103285Sikob                    }
288103285Sikob                }
289103285Sikob            } else {
290103285Sikob                if (encrypt) {
291103285Sikob                    // generate IV if none supplied for encryption
292103285Sikob                    ivBytes = new byte[blockSize];
293103285Sikob                    if (random == null) {
294103285Sikob                        random = JCAUtil.getSecureRandom();
295111615Ssimokawa                    }
296111615Ssimokawa                    random.nextBytes(ivBytes);
297111615Ssimokawa                } else {
298111615Ssimokawa                    throw new InvalidAlgorithmParameterException
299111615Ssimokawa                            ("Parameters required for decryption");
300113584Ssimokawa                }
301113584Ssimokawa            }
302103285Sikob        }
303103285Sikob        init(doEncrypt, key.getEncoded().clone(), ivBytes);
304103285Sikob    }
305103285Sikob
306103285Sikob    // see JCE spec
307111615Ssimokawa    @Override
308103285Sikob    protected synchronized void engineInit(int opmode, Key key,
309103285Sikob            AlgorithmParameters params, SecureRandom random)
310103285Sikob            throws InvalidKeyException, InvalidAlgorithmParameterException {
311111615Ssimokawa        AlgorithmParameterSpec spec = null;
312111615Ssimokawa        if (params != null) {
313103285Sikob            try {
314103285Sikob                spec = params.getParameterSpec(IvParameterSpec.class);
315103285Sikob            } catch (InvalidParameterSpecException iaps) {
316114732Ssimokawa                throw new InvalidAlgorithmParameterException(iaps);
317103285Sikob            }
318103285Sikob        }
319103285Sikob        engineInit(opmode, key, spec, random);
320113584Ssimokawa    }
321103285Sikob
322103285Sikob    // see JCE spec
323103285Sikob    @Override
324113584Ssimokawa    protected synchronized byte[] engineUpdate(byte[] in, int ofs, int len) {
325103285Sikob        byte[] out = new byte[getOutputSizeByOperation(len, false)];
326111615Ssimokawa        int n = update(in, ofs, len, out, 0);
327110145Ssimokawa        if (n == 0) {
328114732Ssimokawa            return null;
329103285Sikob        } else if (out.length != n) {
330111615Ssimokawa            out = Arrays.copyOf(out, n);
331111615Ssimokawa        }
332111615Ssimokawa        return out;
333111615Ssimokawa    }
334103285Sikob
335108281Ssimokawa    // see JCE spec
336103285Sikob    @Override
337103285Sikob    protected synchronized int engineUpdate(byte[] in, int inOfs, int inLen,
338103285Sikob        byte[] out, int outOfs) throws ShortBufferException {
339103285Sikob        int min = getOutputSizeByOperation(inLen, false);
340111615Ssimokawa        if (out.length - outOfs < min) {
341111615Ssimokawa            throw new ShortBufferException("min " + min + "-byte buffer needed");
342103285Sikob        }
343103285Sikob        return update(in, inOfs, inLen, out, outOfs);
344103285Sikob    }
345103285Sikob
346103285Sikob    // see JCE spec
347103285Sikob    @Override
348103285Sikob    protected synchronized void engineUpdateAAD(byte[] src, int ofs, int len)
349103285Sikob            throws IllegalStateException {
350103285Sikob        throw new IllegalStateException("No AAD can be supplied");
351103285Sikob    }
352103285Sikob
353103285Sikob    // see JCE spec
354103285Sikob    @Override
355103285Sikob    protected void engineUpdateAAD(ByteBuffer src)
356103285Sikob            throws IllegalStateException {
357103285Sikob        throw new IllegalStateException("No AAD can be supplied");
358103285Sikob    }
359103285Sikob
360103285Sikob    // see JCE spec
361103285Sikob    @Override
362103285Sikob    protected synchronized byte[] engineDoFinal(byte[] in, int ofs, int len)
363103285Sikob            throws IllegalBlockSizeException, BadPaddingException {
364103285Sikob        byte[] out = new byte[getOutputSizeByOperation(len, true)];
365103285Sikob        try {
366103285Sikob            // delegate to the other engineDoFinal(...) method
367103285Sikob            int k = engineDoFinal(in, ofs, len, out, 0);
368103285Sikob            if (out.length != k) {
369103285Sikob                out = Arrays.copyOf(out, k);
370103285Sikob            }
371103285Sikob            return out;
372103285Sikob        } catch (ShortBufferException e) {
373103285Sikob            throw new UcryptoException("Internal Error", e);
374103285Sikob        }
375103285Sikob    }
376103285Sikob
377103285Sikob    // see JCE spec
378103285Sikob    @Override
379103285Sikob    protected synchronized int engineDoFinal(byte[] in, int inOfs, int inLen,
380103285Sikob                                             byte[] out, int outOfs)
381103285Sikob            throws ShortBufferException, IllegalBlockSizeException,
382103285Sikob            BadPaddingException {
383103285Sikob        int k = 0;
384103285Sikob        int min = getOutputSizeByOperation(inLen, true);
385103285Sikob        if (out.length - outOfs < min) {
386103285Sikob            throw new ShortBufferException("min " + min + "-byte buffer needed");
387103285Sikob        }
388103285Sikob        if (inLen > 0) {
389103285Sikob            k = update(in, inOfs, inLen, out, outOfs);
390103285Sikob            outOfs += k;
391103285Sikob        }
392103285Sikob        k += doFinal(out, outOfs);
393103285Sikob        return k;
394103285Sikob    }
395103285Sikob
396103285Sikob
397103285Sikob    // see JCE spec
398103285Sikob    @Override
399103285Sikob    protected synchronized byte[] engineWrap(Key key)
400103285Sikob            throws IllegalBlockSizeException, InvalidKeyException {
401103285Sikob        byte[] result = null;
402103285Sikob        try {
403103285Sikob            byte[] encodedKey = key.getEncoded();
404103285Sikob            if ((encodedKey == null) || (encodedKey.length == 0)) {
405103285Sikob                throw new InvalidKeyException("Cannot get an encoding of " +
406103285Sikob                                              "the key to be wrapped");
407103285Sikob            }
408103285Sikob            result = engineDoFinal(encodedKey, 0, encodedKey.length);
409103285Sikob        } catch (BadPaddingException e) {
410103285Sikob            // Should never happen for key wrapping
411103285Sikob            throw new UcryptoException("Internal Error" , e);
412103285Sikob        }
413103285Sikob        return result;
414103285Sikob    }
415103285Sikob
416111615Ssimokawa    // see JCE spec
417111615Ssimokawa    @Override
418111615Ssimokawa    protected synchronized Key engineUnwrap(byte[] wrappedKey,
419103285Sikob            String wrappedKeyAlgorithm, int wrappedKeyType)
420103285Sikob            throws InvalidKeyException, NoSuchAlgorithmException {
421103285Sikob
422103285Sikob        byte[] encodedKey;
423103285Sikob        Key result = null;
424103285Sikob        try {
425103285Sikob            encodedKey = engineDoFinal(wrappedKey, 0,
426103285Sikob                                       wrappedKey.length);
427103285Sikob        } catch (Exception e) {
428103285Sikob            throw (InvalidKeyException)
429103285Sikob                (new InvalidKeyException()).initCause(e);
430103285Sikob        }
431103285Sikob
432103285Sikob        return constructKey(wrappedKeyType, encodedKey, wrappedKeyAlgorithm);
433103285Sikob    }
434103285Sikob
435103285Sikob    final int checkKey(Key key) throws InvalidKeyException {
436103285Sikob        if (key == null || key.getEncoded() == null) {
437103285Sikob            throw new InvalidKeyException("Key cannot be null");
438108503Ssimokawa        } else {
439108503Ssimokawa            // check key algorithm and format
440103285Sikob            if (!keyAlgo.equalsIgnoreCase(key.getAlgorithm())) {
441103285Sikob                throw new InvalidKeyException("Key algorithm must be " +
442103285Sikob                    keyAlgo);
443103285Sikob            }
444103285Sikob            if (!"RAW".equalsIgnoreCase(key.getFormat())) {
445103285Sikob                throw new InvalidKeyException("Key format must be RAW");
446103285Sikob            }
447103285Sikob            int keyLen = key.getEncoded().length;
448103285Sikob            if (fixedKeySize == -1) {
449103285Sikob                // all 3 AES key lengths are allowed
450103285Sikob                if (keyLen != 16 && keyLen != 24 && keyLen != 32) {
451103285Sikob                    throw new InvalidKeyException("Key size is not valid." +
452103285Sikob                        " Got key length of: " + keyLen);
453103285Sikob                }
454110184Ssimokawa            } else {
455110184Ssimokawa                if (keyLen != fixedKeySize) {
456110184Ssimokawa                    throw new InvalidKeyException("Only " + fixedKeySize +
457110184Ssimokawa                        "-byte keys are accepted. Got: " + keyLen);
458110184Ssimokawa                }
459110184Ssimokawa            }
460110184Ssimokawa            // return the validated key length in bytes
461110184Ssimokawa            return keyLen;
462110184Ssimokawa        }
463110184Ssimokawa    }
464110184Ssimokawa
465110184Ssimokawa    protected void reset(boolean doCancel) {
466110184Ssimokawa        initialized = false;
467110184Ssimokawa        bytesBuffered = 0;
468110184Ssimokawa        if (pCtxt != null) {
469110184Ssimokawa            pCtxt.dispose(doCancel);
470110184Ssimokawa            pCtxt = null;
471110184Ssimokawa        }
472110184Ssimokawa    }
473110184Ssimokawa
474110184Ssimokawa    /**
475110184Ssimokawa     * calls ucrypto_encrypt_init(...) or ucrypto_decrypt_init(...)
476110184Ssimokawa     * @return pointer to the context
477110184Ssimokawa     */
478110184Ssimokawa    protected native static long nativeInit(int mech, boolean encrypt,
479110184Ssimokawa                                            byte[] key, byte[] iv,
480110184Ssimokawa                                            int tagLen, byte[] aad);
481110184Ssimokawa
482110184Ssimokawa    /**
483110184Ssimokawa     * calls ucrypto_encrypt_update(...) or ucrypto_decrypt_update(...)
484110184Ssimokawa     * @return the length of output or if negative, an error status code
485110184Ssimokawa     */
486110184Ssimokawa    private native static int nativeUpdate(long pContext, boolean encrypt,
487110184Ssimokawa                                           byte[] in, int inOfs, int inLen,
488110184Ssimokawa                                           byte[] out, int outOfs);
489110184Ssimokawa
490110184Ssimokawa    /**
491110184Ssimokawa     * calls ucrypto_encrypt_final(...) or ucrypto_decrypt_final(...)
492110184Ssimokawa     * @return the length of output or if negative, an error status code
493110184Ssimokawa     */
494110184Ssimokawa    native static int nativeFinal(long pContext, boolean encrypt,
495110184Ssimokawa                                          byte[] out, int outOfs);
496110184Ssimokawa
497110184Ssimokawa    protected void ensureInitialized() {
498110184Ssimokawa        if (!initialized) {
499110184Ssimokawa            init(encrypt, keyValue, iv);
500110184Ssimokawa            if (!initialized) {
501110184Ssimokawa                throw new UcryptoException("Cannot initialize Cipher");
502110184Ssimokawa            }
503110184Ssimokawa        }
504110184Ssimokawa    }
505110184Ssimokawa
506103285Sikob    protected int getOutputSizeByOperation(int inLen, boolean isDoFinal) {
507103285Sikob        if (inLen <= 0) {
508103285Sikob            inLen = 0;
509108503Ssimokawa        }
510103285Sikob        if (!isDoFinal && (inLen == 0)) {
511103285Sikob            return 0;
512108503Ssimokawa        }
513108503Ssimokawa        return inLen + bytesBuffered;
514103285Sikob    }
515103285Sikob
516103285Sikob    // actual init() implementation - caller should clone key and iv if needed
517103285Sikob    protected void init(boolean encrypt, byte[] keyVal, byte[] ivVal) {
518110184Ssimokawa        reset(true);
519110184Ssimokawa        this.encrypt = encrypt;
520110184Ssimokawa        this.keyValue = keyVal;
521103285Sikob        this.iv = ivVal;
522103285Sikob        long pCtxtVal = nativeInit(mech.value(), encrypt, keyValue, iv, 0, null);
523103285Sikob        initialized = (pCtxtVal != 0L);
524103285Sikob        if (initialized) {
525103285Sikob            pCtxt = new CipherContextRef(this, pCtxtVal, encrypt);
526103285Sikob        } else {
527103285Sikob            throw new UcryptoException("Cannot initialize Cipher");
528111704Ssimokawa        }
529114069Ssimokawa    }
530114069Ssimokawa
531114069Ssimokawa    // Caller MUST check and ensure output buffer has enough capacity
532114069Ssimokawa    private int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) {
533103285Sikob        ensureInitialized();
534103285Sikob        if (inLen <= 0) { return 0; }
535103285Sikob
536103285Sikob        int k = nativeUpdate(pCtxt.id, encrypt, in, inOfs, inLen, out, outOfs);
537103285Sikob        if (k < 0) {
538114069Ssimokawa            reset(false);
539111615Ssimokawa            // cannot throw ShortBufferException here since it's too late
540111704Ssimokawa            // native context is invalid upon any failure
541111704Ssimokawa            throw new UcryptoException(-k);
542111704Ssimokawa        }
543113584Ssimokawa        bytesBuffered += (inLen - k);
544113584Ssimokawa        return k;
545111615Ssimokawa    }
546111615Ssimokawa
547111615Ssimokawa    // Caller MUST check and ensure output buffer has enough capacity
548111615Ssimokawa    private int doFinal(byte[] out, int outOfs) throws IllegalBlockSizeException,
549103285Sikob            BadPaddingException {
550108503Ssimokawa        try {
551108503Ssimokawa            ensureInitialized();
552108503Ssimokawa
553108503Ssimokawa            int k = nativeFinal(pCtxt.id, encrypt, out, outOfs);
554108503Ssimokawa            if (k < 0) {
555108503Ssimokawa                String cause = UcryptoException.getErrorMessage(-k);
556108503Ssimokawa                if (cause.endsWith("_LEN_RANGE")) {
557110839Ssimokawa                    throw new IllegalBlockSizeException(cause);
558108642Ssimokawa                } else if (cause.endsWith("_DATA_INVALID")) {
559108642Ssimokawa                    throw new BadPaddingException(cause);
560108642Ssimokawa                } else {
561108503Ssimokawa                    throw new UcryptoException(-k);
562108503Ssimokawa                }
563108503Ssimokawa            }
564108503Ssimokawa            return k;
565110839Ssimokawa        } finally {
566110839Ssimokawa            reset(false);
567110839Ssimokawa        }
568110839Ssimokawa    }
569108503Ssimokawa}
570103285Sikob