1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package cn.wisenergy.chnmuseum.party.common.video;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public final class AesFlushingCipher {
private final Cipher cipher;
private final int blockSize;
private final byte[] zerosBlock;
private final byte[] flushedBlock;
private int pendingXorBytes;
public AesFlushingCipher(int mode, byte[] secretKey, long nonce, long offset) {
try {
//cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher = Cipher.getInstance("AES/CTR/NoPadding");
blockSize = cipher.getBlockSize();
zerosBlock = new byte[blockSize];
flushedBlock = new byte[blockSize];
long counter = offset / blockSize;
int startPadding = (int) (offset % blockSize);
cipher.init(
mode,
new SecretKeySpec(secretKey, cipher.getAlgorithm().split("/", 2)[0]),
new IvParameterSpec(getInitializationVector(nonce, counter)));
if (startPadding != 0) {
updateInPlace(new byte[startPadding], 0, startPadding);
}
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
}
public void updateInPlace(byte[] data, int offset, int length) {
update(data, offset, length, data, offset);
}
public void update(byte[] in, int inOffset, int length, byte[] out, int outOffset) {
while (pendingXorBytes > 0) {
out[outOffset] = (byte) (in[inOffset] ^ flushedBlock[blockSize - pendingXorBytes]);
outOffset++;
inOffset++;
pendingXorBytes--;
length--;
if (length == 0) {
return;
}
}
int written = nonFlushingUpdate(in, inOffset, length, out, outOffset);
if (length == written) {
return;
}
int bytesToFlush = length - written;
if (!(bytesToFlush < blockSize)) {
throw new IllegalStateException();
}
outOffset += written;
pendingXorBytes = blockSize - bytesToFlush;
written = nonFlushingUpdate(zerosBlock, 0, pendingXorBytes, flushedBlock, 0);
if (!(written == blockSize)) {
throw new IllegalStateException();
}
for (int i = 0; i < bytesToFlush; i++) {
out[outOffset++] = flushedBlock[i];
}
}
private int nonFlushingUpdate(byte[] in, int inOffset, int length, byte[] out, int outOffset) {
try {
return cipher.update(in, inOffset, length, out, outOffset);
} catch (ShortBufferException e) {
throw new RuntimeException(e);
}
}
private byte[] getInitializationVector(long nonce, long counter) {
return ByteBuffer.allocate(16).putLong(nonce).putLong(counter).array();
}
}