Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java
41161 views
1
/*
2
* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package com.sun.crypto.provider;
27
28
import javax.crypto.BadPaddingException;
29
import javax.crypto.CipherSpi;
30
import javax.crypto.IllegalBlockSizeException;
31
import javax.crypto.NoSuchPaddingException;
32
import javax.crypto.ShortBufferException;
33
import java.nio.ByteBuffer;
34
import java.security.AlgorithmParameters;
35
import java.security.GeneralSecurityException;
36
import java.security.InvalidAlgorithmParameterException;
37
import java.security.InvalidKeyException;
38
import java.security.Key;
39
import java.security.NoSuchAlgorithmException;
40
import java.security.ProviderException;
41
import java.security.SecureRandom;
42
import java.security.spec.AlgorithmParameterSpec;
43
import java.util.Arrays;
44
45
/**
46
* This class implements the AES algorithm in its various modes
47
* (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>, <code>CBC</code>,
48
* <code>PCBC</code>) and padding schemes (<code>PKCS5Padding</code>,
49
* <code>NoPadding</code>, <code>ISO10126Padding</code>).
50
*
51
* @author Valerie Peng
52
*
53
*
54
* @see AESCrypt
55
* @see CipherBlockChaining
56
* @see ElectronicCodeBook
57
* @see CipherFeedback
58
* @see OutputFeedback
59
*/
60
61
abstract class AESCipher extends CipherSpi {
62
public static final class General extends AESCipher {
63
public General() {
64
super(-1);
65
}
66
}
67
abstract static class OidImpl extends AESCipher {
68
protected OidImpl(int keySize, String mode, String padding) {
69
super(keySize);
70
try {
71
engineSetMode(mode);
72
engineSetPadding(padding);
73
} catch (GeneralSecurityException gse) {
74
// internal error; re-throw as provider exception
75
ProviderException pe =new ProviderException("Internal Error");
76
pe.initCause(gse);
77
throw pe;
78
}
79
}
80
}
81
public static final class AES128_ECB_NoPadding extends OidImpl {
82
public AES128_ECB_NoPadding() {
83
super(16, "ECB", "NOPADDING");
84
}
85
}
86
public static final class AES192_ECB_NoPadding extends OidImpl {
87
public AES192_ECB_NoPadding() {
88
super(24, "ECB", "NOPADDING");
89
}
90
}
91
public static final class AES256_ECB_NoPadding extends OidImpl {
92
public AES256_ECB_NoPadding() {
93
super(32, "ECB", "NOPADDING");
94
}
95
}
96
public static final class AES128_CBC_NoPadding extends OidImpl {
97
public AES128_CBC_NoPadding() {
98
super(16, "CBC", "NOPADDING");
99
}
100
}
101
public static final class AES192_CBC_NoPadding extends OidImpl {
102
public AES192_CBC_NoPadding() {
103
super(24, "CBC", "NOPADDING");
104
}
105
}
106
public static final class AES256_CBC_NoPadding extends OidImpl {
107
public AES256_CBC_NoPadding() {
108
super(32, "CBC", "NOPADDING");
109
}
110
}
111
public static final class AES128_OFB_NoPadding extends OidImpl {
112
public AES128_OFB_NoPadding() {
113
super(16, "OFB", "NOPADDING");
114
}
115
}
116
public static final class AES192_OFB_NoPadding extends OidImpl {
117
public AES192_OFB_NoPadding() {
118
super(24, "OFB", "NOPADDING");
119
}
120
}
121
public static final class AES256_OFB_NoPadding extends OidImpl {
122
public AES256_OFB_NoPadding() {
123
super(32, "OFB", "NOPADDING");
124
}
125
}
126
public static final class AES128_CFB_NoPadding extends OidImpl {
127
public AES128_CFB_NoPadding() {
128
super(16, "CFB", "NOPADDING");
129
}
130
}
131
public static final class AES192_CFB_NoPadding extends OidImpl {
132
public AES192_CFB_NoPadding() {
133
super(24, "CFB", "NOPADDING");
134
}
135
}
136
public static final class AES256_CFB_NoPadding extends OidImpl {
137
public AES256_CFB_NoPadding() {
138
super(32, "CFB", "NOPADDING");
139
}
140
}
141
public static final class AES128_GCM_NoPadding extends OidImpl {
142
public AES128_GCM_NoPadding() {
143
super(16, "GCM", "NOPADDING");
144
}
145
}
146
public static final class AES192_GCM_NoPadding extends OidImpl {
147
public AES192_GCM_NoPadding() {
148
super(24, "GCM", "NOPADDING");
149
}
150
}
151
public static final class AES256_GCM_NoPadding extends OidImpl {
152
public AES256_GCM_NoPadding() {
153
super(32, "GCM", "NOPADDING");
154
}
155
}
156
157
// utility method used by AESCipher and AESWrapCipher
158
static final void checkKeySize(Key key, int fixedKeySize)
159
throws InvalidKeyException {
160
if (fixedKeySize != -1) {
161
if (key == null) {
162
throw new InvalidKeyException("The key must not be null");
163
}
164
byte[] value = key.getEncoded();
165
if (value == null) {
166
throw new InvalidKeyException("Key encoding must not be null");
167
} else {
168
Arrays.fill(value, (byte)0);
169
if (value.length != fixedKeySize) {
170
throw new InvalidKeyException("The key must be " +
171
fixedKeySize + " bytes");
172
}
173
}
174
}
175
}
176
177
/*
178
* internal CipherCore object which does the real work.
179
*/
180
private CipherCore core = null;
181
182
/*
183
* needed to support AES oids which associates a fixed key size
184
* to the cipher object.
185
*/
186
private final int fixedKeySize; // in bytes, -1 if no restriction
187
188
/*
189
* needed to enforce ISE thrown when updateAAD is called after update for GCM mode.
190
*/
191
private boolean updateCalled;
192
193
/**
194
* Creates an instance of AES cipher with default ECB mode and
195
* PKCS5Padding.
196
*/
197
protected AESCipher(int keySize) {
198
core = new CipherCore(new AESCrypt(), AESConstants.AES_BLOCK_SIZE);
199
fixedKeySize = keySize;
200
}
201
202
/**
203
* Sets the mode of this cipher.
204
*
205
* @param mode the cipher mode
206
*
207
* @exception NoSuchAlgorithmException if the requested cipher mode does
208
* not exist
209
*/
210
protected void engineSetMode(String mode)
211
throws NoSuchAlgorithmException {
212
core.setMode(mode);
213
}
214
215
/**
216
* Sets the padding mechanism of this cipher.
217
*
218
* @param paddingScheme the padding mechanism
219
*
220
* @exception NoSuchPaddingException if the requested padding mechanism
221
* does not exist
222
*/
223
protected void engineSetPadding(String paddingScheme)
224
throws NoSuchPaddingException {
225
core.setPadding(paddingScheme);
226
}
227
228
/**
229
* Returns the block size (in bytes).
230
*
231
* @return the block size (in bytes), or 0 if the underlying algorithm is
232
* not a block cipher
233
*/
234
protected int engineGetBlockSize() {
235
return AESConstants.AES_BLOCK_SIZE;
236
}
237
238
/**
239
* Returns the length in bytes that an output buffer would need to be in
240
* order to hold the result of the next <code>update</code> or
241
* <code>doFinal</code> operation, given the input length
242
* <code>inputLen</code> (in bytes).
243
*
244
* <p>This call takes into account any unprocessed (buffered) data from a
245
* previous <code>update</code> call, and padding.
246
*
247
* <p>The actual output length of the next <code>update</code> or
248
* <code>doFinal</code> call may be smaller than the length returned by
249
* this method.
250
*
251
* @param inputLen the input length (in bytes)
252
*
253
* @return the required output buffer size (in bytes)
254
*/
255
protected int engineGetOutputSize(int inputLen) {
256
return core.getOutputSize(inputLen);
257
}
258
259
/**
260
* Returns the initialization vector (IV) in a new buffer.
261
*
262
* <p>This is useful in the case where a random IV has been created
263
* (see <a href = "#init">init</a>),
264
* or in the context of password-based encryption or
265
* decryption, where the IV is derived from a user-provided password.
266
*
267
* @return the initialization vector in a new buffer, or null if the
268
* underlying algorithm does not use an IV, or if the IV has not yet
269
* been set.
270
*/
271
protected byte[] engineGetIV() {
272
return core.getIV();
273
}
274
275
/**
276
* Returns the parameters used with this cipher.
277
*
278
* <p>The returned parameters may be the same that were used to initialize
279
* this cipher, or may contain the default set of parameters or a set of
280
* randomly generated parameters used by the underlying cipher
281
* implementation (provided that the underlying cipher implementation
282
* uses a default set of parameters or creates new parameters if it needs
283
* parameters but was not initialized with any).
284
*
285
* @return the parameters used with this cipher, or null if this cipher
286
* does not use any parameters.
287
*/
288
protected AlgorithmParameters engineGetParameters() {
289
return core.getParameters("AES");
290
}
291
292
/**
293
* Initializes this cipher with a key and a source of randomness.
294
*
295
* <p>The cipher is initialized for one of the following four operations:
296
* encryption, decryption, key wrapping or key unwrapping, depending on
297
* the value of <code>opmode</code>.
298
*
299
* <p>If this cipher requires an initialization vector (IV), it will get
300
* it from <code>random</code>.
301
* This behaviour should only be used in encryption or key wrapping
302
* mode, however.
303
* When initializing a cipher that requires an IV for decryption or
304
* key unwrapping, the IV
305
* (same IV that was used for encryption or key wrapping) must be provided
306
* explicitly as a
307
* parameter, in order to get the correct result.
308
*
309
* <p>This method also cleans existing buffer and other related state
310
* information.
311
*
312
* @param opmode the operation mode of this cipher (this is one of
313
* the following:
314
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
315
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
316
* @param key the secret key
317
* @param random the source of randomness
318
*
319
* @exception InvalidKeyException if the given key is inappropriate for
320
* initializing this cipher
321
*/
322
protected void engineInit(int opmode, Key key, SecureRandom random)
323
throws InvalidKeyException {
324
checkKeySize(key, fixedKeySize);
325
updateCalled = false;
326
core.init(opmode, key, random);
327
}
328
329
/**
330
* Initializes this cipher with a key, a set of
331
* algorithm parameters, and a source of randomness.
332
*
333
* <p>The cipher is initialized for one of the following four operations:
334
* encryption, decryption, key wrapping or key unwrapping, depending on
335
* the value of <code>opmode</code>.
336
*
337
* <p>If this cipher (including its underlying feedback or padding scheme)
338
* requires any random bytes, it will get them from <code>random</code>.
339
*
340
* @param opmode the operation mode of this cipher (this is one of
341
* the following:
342
* <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
343
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
344
* @param key the encryption key
345
* @param params the algorithm parameters
346
* @param random the source of randomness
347
*
348
* @exception InvalidKeyException if the given key is inappropriate for
349
* initializing this cipher
350
* @exception InvalidAlgorithmParameterException if the given algorithm
351
* parameters are inappropriate for this cipher
352
*/
353
protected void engineInit(int opmode, Key key,
354
AlgorithmParameterSpec params,
355
SecureRandom random)
356
throws InvalidKeyException, InvalidAlgorithmParameterException {
357
checkKeySize(key, fixedKeySize);
358
updateCalled = false;
359
core.init(opmode, key, params, random);
360
}
361
362
protected void engineInit(int opmode, Key key,
363
AlgorithmParameters params,
364
SecureRandom random)
365
throws InvalidKeyException, InvalidAlgorithmParameterException {
366
checkKeySize(key, fixedKeySize);
367
updateCalled = false;
368
core.init(opmode, key, params, random);
369
}
370
371
/**
372
* Continues a multiple-part encryption or decryption operation
373
* (depending on how this cipher was initialized), processing another data
374
* part.
375
*
376
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
377
* buffer, starting at <code>inputOffset</code>, are processed, and the
378
* result is stored in a new buffer.
379
*
380
* @param input the input buffer
381
* @param inputOffset the offset in <code>input</code> where the input
382
* starts
383
* @param inputLen the input length
384
*
385
* @return the new buffer with the result
386
*
387
* @exception IllegalStateException if this cipher is in a wrong state
388
* (e.g., has not been initialized)
389
*/
390
protected byte[] engineUpdate(byte[] input, int inputOffset,
391
int inputLen) {
392
updateCalled = true;
393
return core.update(input, inputOffset, inputLen);
394
}
395
396
/**
397
* Continues a multiple-part encryption or decryption operation
398
* (depending on how this cipher was initialized), processing another data
399
* part.
400
*
401
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
402
* buffer, starting at <code>inputOffset</code>, are processed, and the
403
* result is stored in the <code>output</code> buffer, starting at
404
* <code>outputOffset</code>.
405
*
406
* @param input the input buffer
407
* @param inputOffset the offset in <code>input</code> where the input
408
* starts
409
* @param inputLen the input length
410
* @param output the buffer for the result
411
* @param outputOffset the offset in <code>output</code> where the result
412
* is stored
413
*
414
* @return the number of bytes stored in <code>output</code>
415
*
416
* @exception ShortBufferException if the given output buffer is too small
417
* to hold the result
418
*/
419
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
420
byte[] output, int outputOffset)
421
throws ShortBufferException {
422
updateCalled = true;
423
return core.update(input, inputOffset, inputLen, output,
424
outputOffset);
425
}
426
427
428
/**
429
* Encrypts or decrypts data in a single-part operation,
430
* or finishes a multiple-part operation.
431
* The data is encrypted or decrypted, depending on how this cipher was
432
* initialized.
433
*
434
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
435
* buffer, starting at <code>inputOffset</code>, and any input bytes that
436
* may have been buffered during a previous <code>update</code> operation,
437
* are processed, with padding (if requested) being applied.
438
* The result is stored in a new buffer.
439
*
440
* <p>The cipher is reset to its initial state (uninitialized) after this
441
* call.
442
*
443
* @param input the input buffer
444
* @param inputOffset the offset in <code>input</code> where the input
445
* starts
446
* @param inputLen the input length
447
*
448
* @return the new buffer with the result
449
*
450
* @exception IllegalBlockSizeException if this cipher is a block cipher,
451
* no padding has been requested (only in encryption mode), and the total
452
* input length of the data processed by this cipher is not a multiple of
453
* block size
454
* @exception BadPaddingException if this cipher is in decryption mode,
455
* and (un)padding has been requested, but the decrypted data is not
456
* bounded by the appropriate padding bytes
457
*/
458
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
459
throws IllegalBlockSizeException, BadPaddingException {
460
byte[] out = core.doFinal(input, inputOffset, inputLen);
461
updateCalled = false;
462
return out;
463
}
464
465
/**
466
* Encrypts or decrypts data in a single-part operation,
467
* or finishes a multiple-part operation.
468
* The data is encrypted or decrypted, depending on how this cipher was
469
* initialized.
470
*
471
* <p>The first <code>inputLen</code> bytes in the <code>input</code>
472
* buffer, starting at <code>inputOffset</code>, and any input bytes that
473
* may have been buffered during a previous <code>update</code> operation,
474
* are processed, with padding (if requested) being applied.
475
* The result is stored in the <code>output</code> buffer, starting at
476
* <code>outputOffset</code>.
477
*
478
* <p>The cipher is reset to its initial state (uninitialized) after this
479
* call.
480
*
481
* @param input the input buffer
482
* @param inputOffset the offset in <code>input</code> where the input
483
* starts
484
* @param inputLen the input length
485
* @param output the buffer for the result
486
* @param outputOffset the offset in <code>output</code> where the result
487
* is stored
488
*
489
* @return the number of bytes stored in <code>output</code>
490
*
491
* @exception IllegalBlockSizeException if this cipher is a block cipher,
492
* no padding has been requested (only in encryption mode), and the total
493
* input length of the data processed by this cipher is not a multiple of
494
* block size
495
* @exception ShortBufferException if the given output buffer is too small
496
* to hold the result
497
* @exception BadPaddingException if this cipher is in decryption mode,
498
* and (un)padding has been requested, but the decrypted data is not
499
* bounded by the appropriate padding bytes
500
*/
501
protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
502
byte[] output, int outputOffset)
503
throws IllegalBlockSizeException, ShortBufferException,
504
BadPaddingException {
505
int outLen = core.doFinal(input, inputOffset, inputLen, output,
506
outputOffset);
507
updateCalled = false;
508
return outLen;
509
}
510
511
/**
512
* Returns the key size of the given key object.
513
*
514
* @param key the key object.
515
*
516
* @return the key size of the given key object.
517
*
518
* @exception InvalidKeyException if <code>key</code> is invalid.
519
*/
520
protected int engineGetKeySize(Key key) throws InvalidKeyException {
521
byte[] encoded = key.getEncoded();
522
Arrays.fill(encoded, (byte)0);
523
if (!AESCrypt.isKeySizeValid(encoded.length)) {
524
throw new InvalidKeyException("Invalid AES key length: " +
525
encoded.length + " bytes");
526
}
527
return Math.multiplyExact(encoded.length, 8);
528
}
529
530
/**
531
* Wrap a key.
532
*
533
* @param key the key to be wrapped.
534
*
535
* @return the wrapped key.
536
*
537
* @exception IllegalBlockSizeException if this cipher is a block
538
* cipher, no padding has been requested, and the length of the
539
* encoding of the key to be wrapped is not a
540
* multiple of the block size.
541
*
542
* @exception InvalidKeyException if it is impossible or unsafe to
543
* wrap the key with this cipher (e.g., a hardware protected key is
544
* being passed to a software only cipher).
545
*/
546
protected byte[] engineWrap(Key key)
547
throws IllegalBlockSizeException, InvalidKeyException {
548
return core.wrap(key);
549
}
550
551
/**
552
* Unwrap a previously wrapped key.
553
*
554
* @param wrappedKey the key to be unwrapped.
555
*
556
* @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
557
*
558
* @param wrappedKeyType the type of the wrapped key.
559
* This is one of <code>Cipher.SECRET_KEY</code>,
560
* <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
561
*
562
* @return the unwrapped key.
563
*
564
* @exception NoSuchAlgorithmException if no installed providers
565
* can create keys of type <code>wrappedKeyType</code> for the
566
* <code>wrappedKeyAlgorithm</code>.
567
*
568
* @exception InvalidKeyException if <code>wrappedKey</code> does not
569
* represent a wrapped key of type <code>wrappedKeyType</code> for
570
* the <code>wrappedKeyAlgorithm</code>.
571
*/
572
protected Key engineUnwrap(byte[] wrappedKey,
573
String wrappedKeyAlgorithm,
574
int wrappedKeyType)
575
throws InvalidKeyException, NoSuchAlgorithmException {
576
return core.unwrap(wrappedKey, wrappedKeyAlgorithm,
577
wrappedKeyType);
578
}
579
580
/**
581
* Continues a multi-part update of the Additional Authentication
582
* Data (AAD), using a subset of the provided buffer.
583
* <p>
584
* Calls to this method provide AAD to the cipher when operating in
585
* modes such as AEAD (GCM/CCM). If this cipher is operating in
586
* either GCM or CCM mode, all AAD must be supplied before beginning
587
* operations on the ciphertext (via the {@code update} and {@code
588
* doFinal} methods).
589
*
590
* @param src the buffer containing the AAD
591
* @param offset the offset in {@code src} where the AAD input starts
592
* @param len the number of AAD bytes
593
*
594
* @throws IllegalStateException if this cipher is in a wrong state
595
* (e.g., has not been initialized), does not accept AAD, or if
596
* operating in either GCM or CCM mode and one of the {@code update}
597
* methods has already been called for the active
598
* encryption/decryption operation
599
* @throws UnsupportedOperationException if this method
600
* has not been overridden by an implementation
601
*
602
* @since 1.8
603
*/
604
@Override
605
protected void engineUpdateAAD(byte[] src, int offset, int len) {
606
if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
607
throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
608
}
609
core.updateAAD(src, offset, len);
610
}
611
612
/**
613
* Continues a multi-part update of the Additional Authentication
614
* Data (AAD).
615
* <p>
616
* Calls to this method provide AAD to the cipher when operating in
617
* modes such as AEAD (GCM/CCM). If this cipher is operating in
618
* either GCM or CCM mode, all AAD must be supplied before beginning
619
* operations on the ciphertext (via the {@code update} and {@code
620
* doFinal} methods).
621
* <p>
622
* All {@code src.remaining()} bytes starting at
623
* {@code src.position()} are processed.
624
* Upon return, the input buffer's position will be equal
625
* to its limit; its limit will not have changed.
626
*
627
* @param src the buffer containing the AAD
628
*
629
* @throws IllegalStateException if this cipher is in a wrong state
630
* (e.g., has not been initialized), does not accept AAD, or if
631
* operating in either GCM or CCM mode and one of the {@code update}
632
* methods has already been called for the active
633
* encryption/decryption operation
634
* @throws UnsupportedOperationException if this method
635
* has not been overridden by an implementation
636
*
637
* @since 1.8
638
*/
639
@Override
640
protected void engineUpdateAAD(ByteBuffer src) {
641
if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
642
throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
643
}
644
if (src != null) {
645
int aadLen = src.limit() - src.position();
646
if (aadLen > 0) {
647
if (src.hasArray()) {
648
int aadOfs = Math.addExact(src.arrayOffset(), src.position());
649
core.updateAAD(src.array(), aadOfs, aadLen);
650
src.position(src.limit());
651
} else {
652
byte[] aad = new byte[aadLen];
653
src.get(aad);
654
core.updateAAD(aad, 0, aadLen);
655
}
656
}
657
}
658
}
659
660
/**
661
* Finalize crypto operation with ByteBuffers
662
*
663
* @param input the input ByteBuffer
664
* @param output the output ByteBuffer
665
*
666
* @return output length
667
* @throws ShortBufferException
668
* @throws IllegalBlockSizeException
669
* @throws BadPaddingException
670
*/
671
@Override
672
protected int engineDoFinal(ByteBuffer input, ByteBuffer output)
673
throws ShortBufferException, IllegalBlockSizeException,
674
BadPaddingException {
675
if (core.getMode() == CipherCore.GCM_MODE && !input.hasArray()) {
676
return core.gcmDoFinal(input, output);
677
} else {
678
return super.engineDoFinal(input, output);
679
}
680
}
681
}
682
683