Skip to content

Commit b71af83

Browse files
committed
Fixed AesAudioDecrypt + do not copy buffer
1 parent 17ba408 commit b71af83

5 files changed

Lines changed: 23 additions & 34 deletions

File tree

lib/src/main/java/xyz/gianlu/librespot/audio/cdn/CdnManager.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,7 @@ private Streamer(@NotNull StreamId streamId, @NotNull SuperAudioFormat format, @
241241
available = new boolean[chunks];
242242
requested = new boolean[chunks];
243243

244-
buffer = new byte[chunks][CHUNK_SIZE];
245-
buffer[chunks - 1] = new byte[size % CHUNK_SIZE];
244+
buffer = new byte[chunks][];
246245

247246
this.internalStream = new InternalStream(session.configuration().retryOnChunkError);
248247
writeChunk(firstChunk, 0, fromCache);
@@ -262,7 +261,8 @@ public void writeChunk(@NotNull byte[] chunk, int chunkIndex, boolean cached) th
262261

263262
LOGGER.trace("Chunk {}/{} completed, cached: {}, stream: {}", chunkIndex, chunks, cached, describe());
264263

265-
audioDecrypt.decryptChunk(chunkIndex, chunk, buffer[chunkIndex]);
264+
buffer[chunkIndex] = chunk;
265+
audioDecrypt.decryptChunk(chunkIndex, chunk);
266266
internalStream.notifyChunkAvailable(chunkIndex);
267267
}
268268

lib/src/main/java/xyz/gianlu/librespot/audio/decrypt/AesAudioDecrypt.java

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package xyz.gianlu.librespot.audio.decrypt;
22

3+
import org.jetbrains.annotations.NotNull;
34
import xyz.gianlu.librespot.common.Utils;
45

56
import javax.crypto.Cipher;
@@ -19,33 +20,35 @@
1920
public final class AesAudioDecrypt implements AudioDecrypt {
2021
private static final byte[] AUDIO_AES_IV = new byte[]{(byte) 0x72, (byte) 0xe0, (byte) 0x67, (byte) 0xfb, (byte) 0xdd, (byte) 0xcb, (byte) 0xcf, (byte) 0x77, (byte) 0xeb, (byte) 0xe8, (byte) 0xbc, (byte) 0x64, (byte) 0x3f, (byte) 0x63, (byte) 0x0d, (byte) 0x93};
2122
private final static BigInteger IV_INT = new BigInteger(1, AUDIO_AES_IV);
23+
private static final BigInteger IV_DIFF = BigInteger.valueOf(0x100);
2224
private final SecretKeySpec secretKeySpec;
2325
private final Cipher cipher;
2426
private int decryptCount = 0;
2527
private long decryptTotalTime = 0;
2628

2729
public AesAudioDecrypt(byte[] key) {
28-
this.secretKeySpec = new SecretKeySpec(key, "AES");
2930
try {
31+
this.secretKeySpec = new SecretKeySpec(key, "AES");
3032
this.cipher = Cipher.getInstance("AES/CTR/NoPadding");
31-
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
32-
throw new IllegalStateException(); // This should never happen
33+
} catch (NoSuchAlgorithmException | NoSuchPaddingException ex) {
34+
throw new IllegalStateException(ex); // This should never happen
3335
}
3436
}
3537

3638
@Override
37-
public synchronized void decryptChunk(int chunkIndex, byte[] in, byte[] out) throws IOException {
38-
int pos = CHUNK_SIZE * chunkIndex;
39-
39+
public synchronized void decryptChunk(int chunkIndex, @NotNull byte[] buffer) throws IOException {
40+
BigInteger iv = IV_INT.add(BigInteger.valueOf(CHUNK_SIZE * chunkIndex / 16));
4041
try {
4142
long start = System.nanoTime();
42-
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(Utils.toByteArray(IV_INT.add(BigInteger.valueOf(pos / 16)))));
43+
for (int i = 0; i < buffer.length; i += 4096) {
44+
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(Utils.toByteArray(iv)));
45+
46+
int count = Math.min(4096, buffer.length - i);
47+
int processed = cipher.doFinal(buffer, i, count, buffer, i);
48+
if (count != processed)
49+
throw new IOException(String.format("Couldn't process all data, actual: %d, expected: %d", processed, count));
4350

44-
for (int i = 0; i < in.length; i += 4096) {
45-
int endBytes = Math.min(i + 4096, in.length);
46-
int count = cipher.doFinal(in, 0, endBytes, out, 0);
47-
if (count != endBytes)
48-
throw new IOException(String.format("Couldn't process all data, actual: %d, expected: %d", count, endBytes));
51+
iv = iv.add(IV_DIFF);
4952
}
5053

5154
decryptTotalTime += System.nanoTime() - start;

lib/src/main/java/xyz/gianlu/librespot/audio/decrypt/AudioDecrypt.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* @author Gianlu
77
*/
88
public interface AudioDecrypt {
9-
void decryptChunk(int chunkIndex, byte[] in, byte[] out) throws IOException;
9+
void decryptChunk(int chunkIndex, byte[] buffer) throws IOException;
1010

1111
int decryptTimeMs();
1212
}

lib/src/main/java/xyz/gianlu/librespot/audio/decrypt/NoopAudioDecrypt.java

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,11 @@
11
package xyz.gianlu.librespot.audio.decrypt;
22

3-
import org.apache.logging.log4j.LogManager;
4-
import org.apache.logging.log4j.Logger;
5-
63
/**
74
* @author Gianlu
85
*/
96
public final class NoopAudioDecrypt implements AudioDecrypt {
10-
private final static Logger LOGGER = LogManager.getLogger(NoopAudioDecrypt.class);
11-
127
@Override
13-
public void decryptChunk(int chunkIndex, byte[] in, byte[] out) {
14-
int length = in.length;
15-
if (in.length != out.length) {
16-
length = Math.min(in.length, out.length);
17-
LOGGER.warn("Buffers have different lengths! {index: {}, in: {}, out: {}}", chunkIndex, in.length, out.length);
18-
}
19-
20-
System.arraycopy(in, 0, out, 0, length);
8+
public void decryptChunk(int chunkIndex, byte[] buffer) {
219
}
2210

2311
@Override

lib/src/main/java/xyz/gianlu/librespot/audio/storage/AudioFileStreaming.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
import java.util.concurrent.ExecutorService;
2525
import java.util.concurrent.Executors;
2626

27-
import static xyz.gianlu.librespot.audio.storage.ChannelManager.CHUNK_SIZE;
28-
2927
/**
3028
* @author Gianlu
3129
*/
@@ -170,8 +168,7 @@ private class ChunksBuffer implements Closeable {
170168

171169
ChunksBuffer(int size, int chunks) {
172170
this.size = size;
173-
this.buffer = new byte[chunks][CHUNK_SIZE];
174-
this.buffer[chunks - 1] = new byte[size % CHUNK_SIZE];
171+
this.buffer = new byte[chunks][];
175172
this.available = new boolean[chunks];
176173
this.requested = new boolean[chunks];
177174
this.audioDecrypt = new AesAudioDecrypt(key);
@@ -184,7 +181,8 @@ void writeChunk(@NotNull byte[] chunk, int chunkIndex) throws IOException {
184181
if (chunk.length != buffer[chunkIndex].length)
185182
throw new IllegalArgumentException(String.format("Buffer size mismatch, required: %d, received: %d, index: %d", buffer[chunkIndex].length, chunk.length, chunkIndex));
186183

187-
audioDecrypt.decryptChunk(chunkIndex, chunk, buffer[chunkIndex]);
184+
buffer[chunkIndex] = chunk;
185+
audioDecrypt.decryptChunk(chunkIndex, chunk);
188186
internalStream.notifyChunkAvailable(chunkIndex);
189187
}
190188

0 commit comments

Comments
 (0)